ElasticSearch探索之路(三)分布式原理:分布式路由、存储、搜索原理
文章目錄
- 分布式存儲
- 路由
- 新增、索引和刪除文檔
- 取回文檔
- 并發控制
- 分布式搜索
- 查詢階段
- 取回階段
分布式存儲
路由
當索引一個文檔的時候,Elasticsearch會通過哈希來決定將文檔存儲到哪一個主分片中,路由計算公式如下:
shard = hash(routing) % number_of_primary_shards//routing:默認為文檔id,也可以自定義。 //number_of_primary_shards:主分片的數量-
查詢時指定routing:可以直接根據routing信息定位到某個分片查詢,不需要查詢所有的分配,經
過協調節點排序。
-
查詢時不指定routing:因為不知道要查詢的數據具體在哪個分片上,所以整個過程分為 2 個步驟
- 分發:請求到達協調節點后,協調節點將查詢請求分發到每個分片上。
- 聚合:協調節點搜集到每個分片上查詢結果,在將查詢的結果進行排序,之后給用戶返回結果。
從上面的這個公式我們也可以看到一個問題,路由的邏輯與當前主分片的數量強關聯,也就是說如果分片數量變化了,那么所有之前路由的值都會無效,文檔也再也找不到了。這也就是為什么我們要在創建索引的時候就確定好主分片的數量并且永遠不會改變這個數量。
分片數量固定是否意味著會使索引難以進行擴容?
答案是否定的,Elasticsearch還提供了其他的一些方案來讓我們輕松的實現擴容,如:
- 分片預分配:一個分片存在于單個節點,但一個節點可以持有多個分片。因此我們可以根據未來的數據的擴張狀況來預先分配一定數量的分片到各個節點中。(注意??:預先分配過多的分片會導致性能的下降以及影響搜索結果的相關度)
- 新建索引:分片數不夠時,可以考慮新建索引,搜索1個有著50個分片的索引與搜索50個每個都有1個分片的索引完全等價。
更多關于水平拓展的內容可以參考官方文檔擴容設計。
新增、索引和刪除文檔
我們可以發送請求到集群中的任一節點。 每個節點都有能力處理任意請求。 每個節點都知道集群中任一文檔位置,所以可以直接將請求轉發到需要的節點上。 在下面的例子中,將所有的請求發送到 Node 1 ,我們將其稱為協調節點(coordinating node) 。
當發送請求的時候, 為了擴展負載,更好的做法是輪詢集群中所有的節點。
新建、索引和刪除請求都是寫操作, 必須在主分片上面完成之后才能被復制到相關的副本分片。
流程如下:
取回文檔
由于取回文檔為讀操作,我們可以從主分片或者從其它任意副本分片檢索文檔。
流程如下:
在處理讀取請求時,協調結點在每次請求的時候都會通過輪詢所有的副本分片來達到負載均衡。
并發控制
在數據庫領域中,有兩種方法通常被用來確保并發更新時變更不會丟失:
- 悲觀并發控制:這種方法被關系型數據庫廣泛使用,它假定有變更沖突可能發生,因此阻塞訪問資源以防止沖突。 一個典型的例子是讀取一行數據之前先將其鎖住,確保只有放置鎖的線程能夠對這行數據進行修改。
- 樂觀并發控制:Elasticsearch中使用的這種方法假定沖突是不可能發生的,并且不會阻塞正在嘗試的操作。 然而,如果源數據在讀寫當中被修改,更新將會失敗。應用程序接下來將決定該如何解決沖突。 例如,可以重試更新、使用新的數據、或者將相關情況報告給用戶。
Elasticsearch是分布式的。當文檔創建、更新或刪除時, 新版本的文檔必須復制到集群中的其他節點。Elasticsearch也是異步和并發的,這意味著這些復制請求被并行發送,并且到達目的地時也許會亂序。所以Elasticsearch 需要一種方法確保文檔的舊版本不會覆蓋新的版本。
在Elasticsearch中,其通過版本號機制來實現樂觀并發控制。即每一個文檔中都會有一個_version版本號字段,當文檔被修改時版本號遞增。 Elasticsearch使用_version來確保變更以正確順序得到執行。如果舊版本的文檔在新版本之后到達,它可以被簡單的忽略。
我們可以利用_version號來確保應用中相互沖突的變更不會導致數據丟失。我們通過指定想要修改文檔的 version 號來達到這個目的。 如果該版本不是當前版本號,我們的請求將會失敗。
// 例如我們想更新文檔的內容,并指定版本號為1 PUT /website/blog/1?version=1 {"title": "My first blog entry","text": "Starting to get the hang of this..." }// 當文檔的版本號為1時,次請求成功,同時響應體告訴我們版本號遞增到2 {"_index": "website","_type": "blog","_id": "1","_version": 2"created": false }// 此時我們再次嘗試更新文檔的內容,仍然指定版本號為1,由于版本號不符合,此時返回409 Conflict HTTP 響應碼 {"error": {"root_cause": [{"type": "version_conflict_engine_exception","reason": "[blog][1]: version conflict, current [2], provided [1]","index": "website","shard": "3"}],"type": "version_conflict_engine_exception","reason": "[blog][1]: version conflict, current [2], provided [1]","index": "website","shard": "3"},"status": 409 }分布式搜索
搜索需要一種更加復雜的執行模型,因為我們不知道查詢會命中哪些文檔,這些文檔有可能在集群的任何分片上。 一個搜索請求必須詢問我們關注的索引的所有分片的某個副本來確定它們是否含有任何匹配的文檔。
但是找到所有的匹配文檔僅僅完成事情的一半。 在 search 接口返回一個 page 結果之前,多分片中的結果必須組合成單個排序列表。 為此,搜索被執行成一個兩階段過程,我們稱之為query then fetch(查詢后取回)。
查詢階段
在查詢階段時, 查詢會廣播到索引中每一個分片拷貝(主分片或者副本分片)。 每個分片在本地執行搜索并構建一個匹配文檔的優先隊列。
查詢階段包含以下三個步驟
一個索引可以由一個或幾個主分片組成, 所以一個針對單個索引的搜索請求需要能夠把來自多個分片的結果組合起來。 針對 multiple 或者 all 索引的搜索工作方式也是完全一致的——僅僅是包含了更多的分片而已。
取回階段
在查詢階段中,我們標識了哪些文檔滿足搜索請求,而接下來我們就需要取回這些文檔。
取回階段由以下步驟構成
總結
以上是生活随笔為你收集整理的ElasticSearch探索之路(三)分布式原理:分布式路由、存储、搜索原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ElasticSearch探索之路(一)
- 下一篇: ElasticSearch探索之路(四)