Elasticsearch系列---補充幾個知識點


概要

bulk api有趣的json格式

前面《簡單入門實戰》一節中,有介紹bulk的使用示例,大家一定很奇怪,還有這么有趣的JSON格式,必須嚴格照他的換行來做,我想把JSON搞得美觀可讀性好一點,居然給我報錯!

{"action": {"meta"}}\n
{"data"}\n
{"action": {"meta"}}\n
{"data"}\n

它為什么要這樣規定?

我們想想bulk設計的初衷,批處理的執行效率肯定是第一優先級,此時效率>可讀性,如果我們允許隨意換行,用標准格式的JSON串,會有什么區別?

如果是標准格式的JSON串,處理流程一般會是這樣:

  1. 將整個json數組全部加載,解析為JSONArray對象,這時內存中同時有json串文本和JSONArray對象。
  2. 循環遍歷JSONArray對象,獲取每個請求中的document進行路由信息。
  3. 把路由到同一個shard的請求合在一組,開辟一個新的請求數組,將JSONObject放在數組里。
  4. 序列化請求數組,發送到對應的節點上去。
  5. 收集各節點的響應,匯總后返回給Coordinate Node。
  6. Coordinate Node收到所有的匯總信息,返回給客戶端。

這種方式唯一的缺點就是占用內存多,一份json串,解析為JSONArray對象,內存占用翻番,bulk里面多則幾千條請求,如果JSON報文大一點,這內存耗費不是開玩笑的,如果bulk占用的內存過多,就可能會擠壓其他請求的內存使用量,如搜索請求、數據分析請求等,整體性能會急速下降,嚴重的情況可能會觸發Full GC,會導致整個JVM工作線程暫停。

再看看現有的格式定義:除了delete操作占一行,其他操作都是占兩行的,ES收到bulk請求時,就可以簡單的按行進行切割,也不用轉成json對象了,切割完的JSON讀取里面的meta信息,直接路由到相應的shard,收集完響應返回即可。
這樣的好處切割邏輯更簡單,都是處理小json字符串,內存快拿快放,整個ES避免對內存的大塊占用,盡可能保證性能。

增刪改文檔內部原理

增刪改的過程整體與查詢文檔過程一致,只是多了一個數據同步的步驟,整個過程如圖所示:

增刪改過程圖示

相似的步驟不贅述。
步驟3的前提是primary shard操作成功,異步請求,所有的replica都返回成功后,node2響應操作成功的消息給Coordinate Node,最后Coordinate Node向客戶端返回成功消息,此時所有的primary shard和replica shard均已完成數據同步,數據是一致的。

查詢文檔內部原理

當我們使用客戶端(Java或Restful API)向Elasticsearch搜索文檔數據時,可以向任意一個node發送請求,此時接受請求的node就是Coordinate Node,整個過程如圖所示:

查詢過程圖示

  1. Coordinate Node接收到請求后,根據_id信息或routing信息,確定該document的路由信息,即在哪個shard里,比如說P0。
  2. Coordinate Node轉發請求,使用round-robin隨機輪詢算法 ,在primary shard或replica shard隨機挑一個,讓讀請求負載均衡,如node-3的R0-1
  3. 接收請求的node-3搜索完成后,響應結果給Coordinate Node。
  4. Coordinate Node將響應結果返回給客戶端。

注意一個問題,如果document還在建立索引過程中,可能只有primary shard有,任何一個replica shard都沒有,此時可能會無法讀取到document,但是等document完成索引建立后,primary shard和replica shard就都有了,這個時間間隔,大概1秒左右。

寫一致性要求

Elasticsearch在嘗試執行一個寫操作時,可以帶上consistency參數,聲明我們的寫一致性的級別,正確地使用這個級別,為了避免因分區故障執行寫操作,導致數據不一致,這個參數有三個值供選擇:

  • one:只要有一個primary shard是active活躍可用的,就可以執行寫操作
  • all:必須所有的primary shard和replica shard都是活躍的,才可以執行這個寫操作
  • quorum:默認的值,要求所有的shard中,必須是大部分的shard都是活躍的,可用的,才可以執行這個寫操作
這個大部分,該怎么算呢?

這個大部分,叫規定數量(quorum),有個計算公式:

int( (primary + number_of_replicas) / 2 ) + 1

  • primary 即一個索引下的primary shard數量;
  • number_of_replicas即每個primary shard擁有的副本數量,注意不是一個索引所有的副本數量。

如果一個索引有3個primary shard,每個shard擁有1個replica shard,共6個shard,這樣number_of_replicas就是1,代入公式計算:
quorum = int ((3 + 1) / 2) + 1 = 3

所以6個shard中必須有3個是活躍的,才讓你寫,如果你只啟用2個node,這樣活躍的replica shard只會有1個,加上primarys shard ,結果最多是2。這樣是達不到quorun的值,因此將無法索引和刪除任何文檔。
此時你必須啟動3個節點,才能滿足quorum寫一致性的要求。

quorum不夠時的超時處理

如果寫操作檢查前,活躍的shard不夠導致無法寫入時,Elasticsearch會等待,希望宕機的node能夠恢復,默認60秒,可以使用timeout參數修改默認值。

單node的寫一致性

照上面的公式算,1個node的,1個索引1個primary shard,number_of_replicas為1的情況,計算公式:

quorum = int ((1 + 1) / 2) + 1 = 2

實際只有一個primary shard是活躍的,豈不是永遠無法寫入?我研發機器只啟動一個node,不照樣增刪改查?

原來是Elasticsearch為了避免單一node的無法寫入問題,加了判斷邏輯:只有number_of_replicas大於1的時候,quorum才會生效。

小結

本篇從性能優先的角度簡單對bulk的設計作了一些補充,並對文檔查詢,寫操作的原理過程,一致性級別,quorum的計算做了一些簡單講解,謝謝。

專注Java高並發、分布式架構,更多技術干貨分享與心得,請關注公眾號:Java架構社區
Java架構社區


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM