Elasticsearch是一個近實時分散式搜尋引擎,其底層基於開源全文搜尋庫Lucene;Elasticsearch對Lucene進行封裝,對外提供REST API 的操作介面。基於 ES,可以快速的搭建全文搜尋引擎;除了搜尋功能,ES還可以對資料進行分析:如日誌分析、指標分析,同時還提供了機器學習功能。Elasticsearch有一個完整的生態圈(ELK),形成了從資料採集(logstash,filebeat)、資料儲存(Elasticsearch)、資料視覺化(kibana)的閉環。
下面簡單介紹一下ES中的基礎概念:
1.ES叢集
Elasticsearch是一個分散式系統,具有高可用性及可擴充套件性,當叢集中有節點停止或丟失時不會影響叢集服務或造成資料丟失;同時當訪問量或資料量增加時可用採用橫向擴充套件的方式增加節點,將請求或資料分散到叢集的各個節點上。不同的叢集可以透過不同的名字來區分,叢集預設名為“elasticsearch“,如果節點配置的叢集名稱一樣,則這些節點組成為一個ES叢集。
2.ES節點
一個節點是一個ElasticSearch的例項,本質上是一個Java程序。ES根據功能不同分為不同的節點型別,在生產環境中,建議根據資料量,寫入及查詢吞吐量,選擇合適的部署方式,最好將節點設定為單一角色。
3.ES文件
文件是ES的最小單位,通常用JSON方式的資料結構表示,類似於資料庫中的一條記錄。文件具有以下特徵:
1.自我包含,一篇文件同時包含欄位和它們的取值。
2.層次型結構,文件中可以包含新的文件。
3.靈活的結構,不依賴於預先定義的模式,文件是無模式的,並非所有的文件都需要擁有相同的欄位。
4.ES型別
型別是文件的邏輯容器,類似於資料庫中的表,型別在 Elasticsearch中表示一類相似的文件,每個型別中欄位的定義稱為對映。ES7.x已經將型別移除,7.x中一個索引只能有一個型別,預設為_doc。
5.ES對映
mapping對映, 就像資料庫中的 schema ,定義索引中欄位的名稱、欄位的資料型別(如 string, integer 或 date),設定欄位倒排索引的相關配置。當索引文件遇到未定義的欄位,會使用dynamic mapping 來確定欄位的資料型別,並自動把新增加的欄位新增到型別對映。在實際生產中一般或禁用dynamic mapping,避免過多的欄位導致cluster state佔用過多,同時禁止自動建立索引的功能,建立索引時必須提供Mapping資訊或者透過Index Template建立。
6.ES索引
ES索引是對映型別的容器,類似於資料庫。
7.ES分片
一個分片是一個執行的Lucene的例項,是一個包含倒排索引的檔案目錄。一個ES索引由一個或多個主分片以及零個或多個副本分片組成,主分片數在索引建立時指定,後續不允許修改;副本分片主要用於解決資料高可用的問題,是主分片的複製,一定程度上提高服務的可讀性。
分片的設定:生產環境中主分片數的設定,需要提前做好容量規劃,因為主分片的數量是不可修改的。如果分片數設定過小,則無法透過增加節點實現水平擴充套件,單個分片的資料量太大,導致資料重新分片耗時;如果分片數設定過大,則會影響搜尋結果的相關性打分,浪費資源,同時影響效能。
在瞭解了ES的基本概念之後,我們透過一張圖來探索一下ES索引的全流程:
ES索引過程詳解:
1.客戶端傳送索引請求
客戶端向ES節點發送索引請求,以RestClient客戶端發起請求為例,ES提供了Java High Level REST Client,可以透過RestClient傳送請求:
RestClient restClient = RestClient.builder( new HttpHost("127.0.0.1", 9200, "http"), new HttpHost("127.0.0.2", 9200, "http") ).build();
其中127.0.0.1,127.0.0.2是ES節點地址,充當coordinate node節點的角色,接收客戶端請求,如果設定有專用coorinate node則應該將接受客戶端請求的節點設定為該專用節點,負責請求的接受和轉發。在RestClient中使用round-robin輪詢演算法,進行傳送節點的選取。
2.引數檢查。
對請求中的引數進行檢查,檢查引數是否合法,不合法的引數直接返回失敗給客戶端。
3.資料預處理
在Ingest Node上有定義好的處理資料的Pipeline,Pipeline中有一組定義好的Processor,每個Processor分別具有不同的處理功能,ES提供了一些內建的Processor,如:split、join、set 、script等,同時也支援透過外掛的方式,實現自定義的Processor。資料經過Pipeline處理完畢後繼續進行下一步操作。
4.判斷索引是否存在
判斷索引是否存在。如果索引不存在,則判斷是否能夠自動建立,可以透過action.auto_create_index設定能否自動建立索引;如果節點支援Dynamic Mapping,寫入文件時,如果欄位尚未在mapping中定義,則會根據索引文件資訊推算欄位的型別,但並不能完全推算正確。
配置Dynamic:true時,文件有新增欄位的時候,索引的mapping也會同步更新。Dynamic:false時,索引的mapping不會被更新,新增欄位無法被索引到。Dynamic:strict時,索引有新增欄位時,將會報錯。
5.建立索引
建立索引請求被髮送到Master節點,由Master節點負責進行索引的建立,索引建立成功後,Master節點會更新叢集狀態clusterstate,更新完畢後將索引建立的情況返回給Coordinate節點,收到Master節點返回後,進入下一流程。
6.請求預處理
1)獲取叢集狀態資訊,判斷叢集是否正常;
2)從叢集狀態中獲取對應索引的元資訊,從元資訊中獲取索引的mapping、version等資訊,從請求中解析routing、id資訊,如果請求沒有指定文件的id,則會生成一個UUID作為文件的id。
7.路由計算
根據請求的routing、id資訊計算文件應該被索引到哪個分片,計算公式為:
shard_num = hash(_routing) % num_primary_shards
其中_routing預設值為文件id,num_primary_shards是主分片個數,所以從演算法中即可以看出索引的主分片個數一旦指定便無法修改,因為文件利用主分片的個數來進行定位。當使用自定義_routing或者id時,按照上面的公式計算,資料可能會大量聚集於某些分片,造成資料分佈不均衡,所以ES提供了routing_partition_size引數,routing_partition_size越大,資料的分佈越均勻。此時分片的計算公式變為:
shard_num = (hash(_routing) + hash(_id) % routing_partition_size) % num_primary_shards
8.主分片索引文件
當主分片所在節點接受到請求後,節點開始進行本節點的文件寫入,文件寫入過程:
1)文件寫入時,不會直接寫入到磁碟中,而是先將文件寫入到Index Buffer記憶體空間中,到一定的時間,Index Buffer會Refresh把記憶體中的文件寫入Segment中。當文件在Index Buffer中時,是無法被查詢到的,這就是ES不是實時搜尋,而是近實時搜尋的原因。
2)因為文件寫入時,先寫入到記憶體中,當文件落盤之前,節點出現故障重啟、宕機等,會造成記憶體中的資料丟失,所以索引寫入的同時會同步向Transaction Log寫入操作內容。
3)每隔固定的時間間隔ES會將Index Buffer中的文件寫入到Segment中,這個寫入的過程叫做Refresh,Refresh的時間可以透過index.refresh_interval設定,預設情況下為1秒。
4)寫入到Segment中並不代表文件已經落盤,因為Segment寫入磁碟的過程相對耗時,Refresh時會先將Segment寫入快取,開放查詢,也就是說當文件寫入Segment後就可以被查詢到。每次refresh的時候都會生成一個新的segment,太多的Segment會佔用過多的資源,而且每個搜尋請求都會遍歷所有的Segment,Segment過多會導致搜尋變慢,所以ES會定期合併Segment,減少Segment的個數,並將Segment和併為一個大的Segment;在操作Segment時,會維護一個Commit Point檔案,其中記錄了所有Segment的資訊;同時維護.del檔案用於記錄所有刪除的Segment資訊。
單個倒排索引檔案被稱為Segment。多個Segment彙總在一起,就是Lucene的索引,對應的就是ES中的shard。
Lucene倒排索引由單詞詞典及倒排列表組成:
單詞詞典:記錄所有文件的單詞,記錄單詞到倒排列表的關係,資料量比較大,一般採用B+樹,雜湊拉鍊法實現。
倒排列表:記錄單詞對應的文件集合,由倒排索引項組成。倒排索引項結構如表所示:文件ID:記錄單詞所在文件的ID;詞頻:記錄單詞在文件中出現的次數;位置:記錄單詞在文件中的位置;偏移:記錄單詞的開始位置,結束位置。
5)每隔一定的時間(預設30分鐘),ES會呼叫Flush操作,Flush操作會呼叫Refresh將Index Buffer清空;然後呼叫fsync將快取中的Segments寫入磁碟;隨後清空Transaction Log。當Transaction Log空間(預設512M)後也會觸發Flush操作。
9.副本分片索引文件
當主分片完成索引操作後,會迴圈處理要寫的所有副本分片,向副本分片所在的節點發送請求。副本分片執行和主分片一樣的文件寫入流程,然後返回寫入結果給主分片節點。
10.請求返回
主分片收到副本分片的響應後,執行finish()操作,將收到響應資訊返回給Coordinate節點,告知Coordinate節點文件寫入的情況;coordinate節點收到響應後,將索引執行情況返回給客戶端。
至此一個文件索引的全過程結束,使用者可透過ElasticSearch提供的介面進行資料的查詢。
ElasticSearch自誕生以來,使用熱度越來越高,功能越來越強大。ES不僅支援分散式、可擴充套件,還提供了RestFul風格介面,方便應用接入使用;適用於所有的資料型別,具備儲存海量資料能力,擁有高效能的近實時檢索功能,同時還提供了資料的近實時分析功能;適用於海量資料的近實時檢索、分析,在日誌、監控資料儲存分析,集中式全文搜尋方面應用較為廣泛。