1. 針對Elasticsearch並發沖突問題,ES內部是如何解決的?
1)ES內部是線程異步並發修改的,是基於_version版本號進行樂觀鎖並發控制的;
2)若后修改的先到了,那么修改后版本發生變化,先修改的后到發現版本不一致就扔掉了,保證了數據的正確性;
3)primary shard與replica shard同步請求是多線程異步的;
2. 基於版本號的實際操作
1)PUT /index/type/id?version=1;
es中的數據和客戶端的數據的版本號必須是一致的,才能修改;
2)基於external version 進行了樂觀鎖並發控制;
不使用es內部的版本號,使用自己維護的版本號進行並發控制
?version=2&version.type=external
此方法保證只要version比ES中version大,就可以完成修改;
3. partial update
POST /index/type/id/_update
{
"doc":{
要修改的數據。
}
}
和全量替換相比優點:
1)所有的查詢,修改和寫回操作都是發生在es的內部,避免了所在的網絡數據傳輸的開銷,大大提升了性能;
2)減少了查詢和修改的時間間隔,可以減少並發沖突的情況;
partial update的實現原理:
和全量替換差不多:內部先獲取document,將傳過來的field更新到document的json中,將老的document標記為deleted,最后將修改后的新的document創建出來;
partial update的並發控制原理:
內部自動執行並發樂觀鎖的並發控制策略。
POST /index/type/id/_update?retry_on_conflict=5 使用了重試策略,再次去新的版本號在更新;
4. 批量查詢
優點:減少網絡請求的開銷
1) GET /_mget
GET /_mget { "docs" : [ { "_index" : "test_index", "_type" : "test_type", "_id" : 1 }, { "_index" : "test_index", "_type" : "test_type", "_id" : 2 } ] }
2) 如果查詢的document是一個index下的不同type種的話
GET /test_index/_mget { "docs" : [ { "_type" : "test_type", "_id" : 1 }, { "_type" : "test_type", "_id" : 2 } ] }
3) 如果查詢的數據都在同一個index下的同一個type下,最簡單了
GET /test_index/test_type/_mget { "ids": [1, 2] }
5. 批量的增刪改 bulk
POST /_bulk
{ "delete": { "_index": "test_index", "_type": "test_type", "_id": "3" }}
{ "create": { "_index": "test_index", "_type": "test_type", "_id": "12" }}
{ "test_field": "test12" }
{ "index": { "_index": "test_index", "_type": "test_type", "_id": "2" }}
{ "test_field": "replaced test2" }
{ "update": { "_index": "test_index", "_type": "test_type", "_id": "1", "_retry_on_conflict" : 3} }
{ "doc" : {"test_field2" : "bulk test1"} }
(1)delete:刪除一個文檔,只要1個json串就可以了
(2)create:PUT /index/type/id/_create,強制創建
(3)index:普通的put操作,可以是創建文檔,也可以是全量替換文檔
(4)update:執行的partial update操作
bulk api對json的語法,有嚴格的要求,每個json串不能換行,只能放一行,同時一個json串和一個json串之間,必須有一個換行;
bulk操作中,任意一個操作失敗,是不會影響其他的操作的,但是在返回結果里,會告訴你異常日志;
bulk size的最佳大小:
bulk request會加載到內存里,如果太大的話,性能反而會下降,因此需要反復嘗試一個最佳的bulk size。一般從1000~5000條數據開始,嘗試逐漸增加。
6. document的數據路由
路由算法:shard = hash(routing) % number_of_primary_shards
routing值,默認是_id,也可以手動指定,相同的routing值,每次過來,從hash函數中,產出的hash值一定是相同的;
可以手動指定put /index/type/id?routing=user_id ;以保證說,某一類document一定被路由到一個shard上去,那么在后續進行應用級別的負載均衡,以及提升批量讀取的性能的時候,是很有幫助的
這也是 primary shard數量不可變的謎底;
7. 增刪改的內部原理
1)客戶端選擇一個node發送請求過去,這個node就是coordinating node(協調節點);
2)coordinating node,對document進行路由,將請求轉發給對應的node(primary shard);
3)實際的node上的primary shard處理請求,然后將數據同步到replica node;
4)coordinating node,如果發現primary node和所有replica node都搞定之后,就返回響應結果給客戶端;
8. 寫一致性
增刪改操作 put /index/type/id,都可以帶上一個consistency參數 put /index/type/id?consistency=quorum
一致性策略:
one:要求我們這個寫操作,只要有一個primary shard是active活躍可用的,就可以執行
all:要求我們這個寫操作,必須所有的primary shard和replica shard都是活躍的,才可以執行這個寫操作
quorum:默認的值,要求所有的shard中,必須是大部分的shard都是活躍的,可用的,才可以執行這個寫操作
quorum機制,寫之前必須確保大多數shard都可用,int( (primary + number_of_replicas) / 2 ) + 1,當number_of_replicas>1時才生效
quroum = int( (primary + number_of_replicas) / 2 ) + 1
如果節點數少於quorum數量,可能導致quorum不齊全,進而導致無法執行任何寫操作,es提供了一種特殊的處理場景,就是說當number_of_replicas>1時才生效;
quorum不齊全時,會進行wait,默認1分鍾,自己可以設置timeout的時間;
(注意:一個primary shard 的多個replica shard也不能在同一個node上)
9. document 查詢的內部原理
1)客戶端發送請求到任意一個node,成為coordinate node;
2)coordinate node對document進行路由,將請求轉發到對應的node,此時會使用round-robin隨機輪詢算法,在primary shard以及其所有replica中隨機選擇一個,讓讀請求負載均衡;
3)接收請求的node返回document給coordinate node;
4)coordinate node返回document給客戶端;
5)特殊情況:document如果還在建立索引過程中,可能只有primary shard有,任何一個replica shard都沒有,此時可能會導致無法讀取到document,但是document完成索引建立之后,primary shard和replica shard就都有了。
10._bulk api 的奇特JSON格式和性能優化的關系
如果采用比較良好的json數組格式,允許任意的換行,整個可讀性非常棒,讀起來很爽,es要按照下述流程去進行處理:
(1)將json數組解析為JSONArray對象,這個時候,整個數據,就會在內存中出現一份一模一樣的拷貝,一份數據是json文本,一份數據是JSONArray對象;
(2)解析json數組里的每個json,對每個請求中的document進行路由;
(3)為路由到同一個shard上的多個請求,創建一個請求數組;
(4)將這個請求數組序列化;
(5)將序列化后的請求數組發送到對應的節點上去;
耗費更多內存,更多的jvm gc開銷;
奇特的格式:
{"action": {"meta"}}\n
{"data"}\n
(1)不用將其轉換為json對象,不會出現內存中的相同數據的拷貝,直接按照換行符切割json;
(2)對每兩個一組的json,讀取meta,進行document路由;
(3)直接將對應的json發送到node上去;
最大的優勢在於,不需要將json數組解析為一個JSONArray對象,形成一份大數據的拷貝,浪費內存空間,盡可能地保證性能;