Elasticsearch(簡稱 ES)是一款基于 Apache Lucene 的分布式搜索和分析引擎。隨著業務的發展,系統中的數據量不斷增長,傳統的關系型數據庫在處理大量模糊查詢時效率低下。因此,ES 作為一種高效、靈活和可擴展的全文檢索解決方案,逐漸成為了企業的首選。本篇博客將深入探討 Elasticsearch 的核心概念、使用方法以及優化技巧。
為什么要使用Elasticsearch?
系統中的數據,隨著業務的發展和時間的推移,將會變得非常多,而業務中往往都是采用模糊查詢的方式對數據進行搜索,而模糊查詢會導致查詢引擎放棄索引,導致系統查詢數據的時候都是全表掃描,那么在百萬級數據庫中,這樣的查詢效率是非常低下的,而我們使用ES做一個全文索引,將經常查詢的系統功能某些字段,比如說電商系統中的商品名,描述和價格這些字段放入到ES索引庫中,就可以提高查詢效率
并且ES具備以下幾個優勢
1.高性能:ES具有高性能的搜索和分析能力,其中涵蓋了多種查詢語言和數據結構
2.可拓展性:ES是分布式的,可以通過增加節點數量去拓展搜索和分析能力
3.靈活性:ES支持多種數據類型,支持多種語言,支持動態映射,允許快速地調整模型以適應不同地需求
4.實時分析:ES支持實時分析,可以對數據進行實時查詢,這對于快速檢索數據非常有用
5.ES具有可靠性和高可用性,它里面會有冗余備份這樣一個設置支持數據備份和恢復。
正排索引與倒排索引
正排索引:類似于關系型數據庫的存儲方式,它按照文檔順序存儲信息,便于按照文檔查找內容。
倒排索引:適合全文檢索,它記錄了每個詞條在哪些文檔中出現。倒排索引由詞條、詞典和倒排表構成:
- 詞條:最小存儲和查詢單元。
- 詞典:詞條的集合,通常實現為 B+ 樹或哈希表。
- 倒排表:記錄詞條出現的文檔 ID 列表。
倒排索引的設計使得 ES 能夠快速定位和檢索相關文檔,提高查詢效率。
早期的全文檢索會為整個文檔集合建立一個很大的倒排索引并且將其寫入到磁盤,一旦新的索引就緒,舊的索引就會被替代,這樣最近的變化就可以被檢索到,倒排索引被寫入到磁盤后是不可變的,它永遠不會被修改,而是用更多的索引,通過增加補充索引的方式去反映新近的修改,而不是直接重寫整個倒排索引,每一個倒排索引都會被輪流查詢到,從最早的開始的查詢,然后再進行合并
ES基本概念:
Near Realtime(NRT)
近實時:當我們說一個系統或數據庫是近實時的,它意味著從數據被寫入到這些數據可以被檢索或查詢之間有一個很短的延遲。在Elasticsearch中,這個延遲通常非常短,可能只有幾毫秒到幾秒(通常不超過1秒)。這意味著,當你向Elasticsearch中寫入新的數據后,幾乎可以立即查詢這些數據,而不需要等待很長時間。
Index(索引)
索引庫:你可以把索引庫想象成一個巨大的文件柜,里面裝滿了許多不同類別的文件夾。在Elasticsearch中,這些“文件夾”就是索引,而文件夾里的“文件”就是文檔(Documents)。每個索引都包含了一類相似的文檔,比如所有的客戶數據、商品數據或訂單數據都可以分別存儲在各自的索引中,一個索引就類似于關系型數據庫中的一張表。
Type(類型)
類型:在早期的Elasticsearch版本中,每個索引內可以有多個類型,每個類型下的文檔都有相同的字段結構。但隨著時間的推移,Elasticsearch團隊簡化l了這個概念,因為多個類型可能會導致一些復雜性和性能問題。因此,在較新的Elasticsearch版本中,每個索引通常只包含一個類型,但出于兼容性考慮,仍然支持多個類型。但在實際應用中,現在更推薦每個索引只包含一種類型的數據。
Document & Field(文檔 & 字段)
文檔:在Elasticsearch中,文檔是最小的數據單元。你可以把文檔想象成一張表格的一行或一個數據庫記錄。每個文檔都是一個JSON對象,包含了多個字段(Field)。
字段:字段就是文檔中的一個數據項,比如一個文檔可能有一個名為“title”的字段,其值為“The quick brown fox...”。你可以把字段想象成數據庫表中的列。每個文檔可以有不同的字段組合,但通常同一類型的文檔會有相似的字段結構。
映射(Mapping)
mapping是對處理數據的方式和規則方面做一些限制,如:某個字段的數據類型、默認值分析器、是否被索引等等。這些都是映射里面可以設置的,其它就是處理ES 里面數據的些使用規則設置也叫做映射,按著最優規則處理數據對性能提高很大,因此才需要建立映射并且需要思考如何建立映射才能對性能更好。
分片(Shards)
可以理解為mysql中的分表,一個索引中的數據太多了需要進行分片,一個索引可以存儲超出單個節點硬件限制的大量數據。比如,一個具有 10 億文檔數據的索引占據 1TB 的磁盤空間,而任一節點都可能沒有這樣大的磁盤空間。或者單個節點處理搜索請求,響應太慢。為了解決這個問題,Elasticsearch 提供了將索引劃分成多份的能力每一份就稱之為分片。當你創建一個索引的時候,你可以指定你想要的分片的數量,。每個分片本身也是一個功能完善并且獨立的“索引”,這個“索引”可以被放置到集群中的任何節點上面去,分片很重要,主要有兩方面的原因:
1.允許你水平分割/擴展你的內容容量
2.允許你在分片之上進行分布式的、并行的操作,進而提高性能/吞吐量至于一個分片怎樣分布,它的文檔怎樣聚合和搜索請求,是完全由 Elasticsearch 管理的,對于作為用戶的你來說,這些都是透明的,無需過分關心。
副本
在一個網絡/云的環境里,失敗隨時都可能發生,在某個分片/節點不知怎么的就處于離線狀態,或者由于任何原因消失了,這種情況下,有一個故障轉移機制是非常有用并且是強烈推薦的。為此目的,Elastigsearch允許你創建分片的一份或多份拷貝這些拷貝叫做復制分片(副本)。
復制分片之所以重要,有兩個主要原因:
1.在分片/節點失敗的情況下,提供了高可用性,因為這個原因,注意到復制分片從不與原/主要(origimalprimary)分片置于同一節點上是非常重要的。
2.擴展你的搜索量/吞吐量,因為搜索可以在所有的副本上并行運行
路由計算/分片控制
分片控制:用戶可以訪問任何一個節點獲取數據,這個節點稱為協調節點 ,一般是輪詢
模擬寫數據:
用戶發送請求給ES,但是用戶在請求到達ES前是沒辦法獲取到集群狀態的,比如說先到達1002節點,但是1002節點可能會把它分發到其他節點上去
1.客戶端請求集群節點(任意節點)——協調節點
2.協調節點把請求分發到指定節點
3.主分片需要將數據保存
4.主分片需要將數據發送給副本
5.副本保存后進行反饋
6.主分片進行反饋
7.客戶端獲取反饋
注意:主分片會要求在活躍的副本到一定數量的時候才進行寫操作,為了避免在網絡分區故障的時候進行寫操作,導致數據不一致問題。
規定數量:int(primary+number_of_replicas)/2)+1
consistency參數的值可以設為one(只要主分片狀態ok就允許執行寫操作),或者quorum.默認為quorum,就是大多數的分片副本狀態沒有問題就允許執行寫操作。
number_of_replicas指的是在索引設置中設置的副本分片數量,而不是指當前處理活動狀態的副本分片數量
讀數據
1.客戶端首先會發送一個查詢請求到協調節點
2.協調節點會計算數據所在的分片以及全部的副本位置
3.為了可以實現負載均衡,可以輪詢所有節點
4.協調節點將請求轉發給具體的目標節點
5.節點返回查詢結果,將結果反饋給客戶端
更新流程
部分更新一個文檔的步驟如下:
1.客戶端向Node1發送更新請求。
2.將請求轉發到主分片所在的Node3
3.Node3從主分片檢索文檔,修改_source字段中的JSON,并且嘗試重新索引主分片的文檔。如果文檔已經被另外一個線程修改了,就會重試步驟3,超過rety_on_conflict次數后放棄
4.如果Note3成功更新了文檔,他將新的版本文檔并行轉發到Node1和Node2上的副本分配,重新建立索引。一旦所有副本分片都返回成功后,Node3向協調節點也返回成功,協調節點向客戶端返回成功。
注意:這里主版本把跟把更改轉發到副本分片的時候,不會轉發更新請求。相反,他轉發的是完整的新版本文檔。這些請求會異步的被轉發到副本分片,但是不能保證它們按照相同的順序到達。如果ES采用的是轉發更改請求,就有可能會以錯誤的順序去把應用更改,導致得到損壞的文檔
新增詞條?
先進入 ES 根目錄中的 plugins 文件夾下的ik文件夾,進入 config目錄,創建 custom.dic文件,寫入新增的詞條。同時打開IAnalyzer.cfg.xml 文件,將新建的 custom.dic 配置其中重啟 ES 服務器。
自定義分詞器
文檔沖突
?當我們使用IdnexAPI更新文檔的時候,可以一次性讀取原始文檔,去做我們的修改,然后重新索引整個文檔,最近的索引請求將會獲勝,不管最后哪一個文檔被索引了,都會被唯一存儲在ES中。如果其他人同時更改這個索引,那么他們的更改將會丟失。
?很多時候這是沒有問題的,也許我們的主數據存儲是一個關系型數據庫,我們只是把數據 復制到了ES中讓他可以被檢索,也許兩個人同時更改相同文檔的概率很小,或者對于業務來說偶爾丟失更改并不是很嚴重的問題,但有時候局部更新出錯是不能接受的
怎么防止數據更新丟失呢?
悲觀鎖
這種方法被關系型數據庫廣泛使用,它認為沖突必然發生,因此阻塞訪問資源以防止沖突。 一個典型的例子是讀取一行數據之前先將其鎖住,確保只有放置鎖的線程能夠對這行數據進行修改。
樂觀鎖
Elasticsearch 中使用的這種方法假定沖突不是必然發生的,并且不會阻塞正在嘗試的操作,然而,如果源數據在讀寫當中被修改,更新將會失敗。應用程序接下來將決定該如何作。解決沖突。 例如,可以重試更新、使用新的數據、或者將相關情況報告給用戶。
Elasticsearch 是分布式的。當文檔創建、更新或刪除時, 新版本的文檔必須復制到集群中的其他節點。Elasticsearch 也是異步和并發的,這意味著這些復制請求被并行發送,并且到達目的地時也許 順序是亂的。 Elasticsearch 需要一種方法確保文檔的舊版本不會覆蓋新的版本。
當我們之前討論 index ,GET 和 delete 請求時,我們指出每個文檔都有一個 version(版本)號,當文檔被修改時版本號遞增。Elasticsearch 使用這個version 號來確保變更以正確順序得到執行。如果舊版本的文檔在新版本之后到達,它可以被簡單的忽略。
外部系統版本控制
一個常見的設置是使用其它數據庫作為主要的數據存儲,使用 Elasticsearch 做數據檢索, 這意味著主數據庫的所有更改發生時都需要被復制到 Elasticsearch ,如果多個進程負責這一數據同步,你可能遇到類似于之前描述的并發問題。
如果你的主數據庫已經有了版本號-或一個能作為版本號的字段值比如 timestamp那么你就可以在 Elasticsearch 中通過增加 version type=extemmal 到查詢字符串的方式重用這些相同的版本號, 版本號必須是大于零的整數,且小于 9.2E+18-一個 Java 中 long類型的正值。
?