(1)先構造一條數據html
PUT /test_index/_doc/1 { "test_field":"test test" }
(2)模擬兩個客戶端都獲取到同一條數據數據庫
GET /test_index/_doc/1
(3)其中一個客戶端先更新了一下這個數據多線程
PUT /test_index/_doc/1?version=1 { "test_field": "test client 1" }
(4)另一個客戶端,嘗試基於version=1的數據去修改,一樣帶上version版本號,進行樂觀鎖的併發控制併發
PUT /test_index/_doc/1?version=1 { "test_field": "test client 2" }
此時就會報錯version conflict
(5)在樂觀鎖成功阻止併發問題以後,嘗試正確的完成更新elasticsearch
GET /test_index/_doc/1
對於更新失敗的客戶端,查詢出最新的版本號以後,基於最新的數據和版本號去再次進行修改,修改時,帶上最新的版本號,可能這個步驟會須要反覆執行好幾回,才能成功,特別是在多線程併發更新同一條數據很頻繁的狀況下ide
注意特別說明,要完成上面的實驗須要es版本低於6.7。
version Deprecated in 6.7.0. Please use if_seq_no & if_primary_term instead. See Optimistic concurrency control for more details.
這段說得很清楚,高版本是使用if_seg_no和if_primary_term這兩個參數實現的。
下面把上面的實現用高版本作一下。我用的版本是7.0的
(1)先構造一條數據高併發
PUT /test_index/_doc/1 { "test_field":"test test" } { "_index" : "test_index", "_type" : "_doc", "_id" : "1", "_version" : 3, "result" : "created", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 2, "_primary_term" : 1 }
(2)模擬兩個客戶端,都獲取到了同一條數據性能
GET /test_index/_doc/1 { "_index" : "test_index", "_type" : "_doc", "_id" : "1", "_version" : 3, "_seq_no" : 2, "_primary_term" : 1, "found" : true, "_source" : { "test_field" : "test test" } }
(3)其中一個客戶端,先更新了一下這個數據ui
PUT /test_index/_doc/1?if_seq_no=2&if_primary_term=1 { "test_field":"test client 1" } { "_index" : "test_index", "_type" : "_doc", "_id" : "1", "_version" : 4, "result" : "updated", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 3, "_primary_term" : 1 }
(4)另一個客戶端,嘗試基於if_seq_no和if_primary_term的數據進行修改,進行樂觀鎖的併發控制線程
PUT /test_index/_doc/1?if_seq_no=2&if_primary_term=1 { "test_field":"test client 2" } { "error": { "root_cause": [ { "type": "version_conflict_engine_exception", "reason": "[1]: version conflict, required seqNo [2], primary term [1]. current document has seqNo [3] and primary term [1]", "index_uuid": "0jAS2GP1TPG5J8PlqGdYIQ", "shard": "4", "index": "test_index" } ], "type": "version_conflict_engine_exception", "reason": "[1]: version conflict, required seqNo [2], primary term [1]. current document has seqNo [3] and primary term [1]", "index_uuid": "0jAS2GP1TPG5J8PlqGdYIQ", "shard": "4", "index": "test_index" }, "status": 409 }
(5)在樂觀鎖成功阻止併發問題以後,嘗試正確的完成更新
GET /test_index/_doc/1 { "_index" : "test_index", "_type" : "_doc", "_id" : "1", "_version" : 4, "_seq_no" : 3, "_primary_term" : 1, "found" : true, "_source" : { "test_field" : "test client 1" } }
基於最新的數據和if_seq_no,if_primary_term進行修改,可能這個過程會須要反覆執行好幾回,才能成功,特別是在多線程併發更新同一條數據很頻繁的狀況下
PUT /test_index/_doc/1?if_seq_no=3&if_primary_term=1 { "test_field":"test client 2" } { "_index" : "test_index", "_type" : "_doc", "_id" : "1", "_version" : 5, "result" : "updated", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 4, "_primary_term" : 1 } GET /test_index/_doc/1 { "_index" : "test_index", "_type" : "_doc", "_id" : "1", "_version" : 5, "_seq_no" : 4, "_primary_term" : 1, "found" : true, "_source" : { "test_field" : "test client 2" } }
對於if_seq_no和if_primary_term,官方文檔已經有比較詳細的敘述,https://www.elastic.co/guide/... 。這裏我說下簡單的理解方式,對於if_primary_term記錄的就是具體的哪一個主分片,而if_seq_no這個參數起的做用和舊版本中的_version是同樣的,之因此加上if_primary_term這個參數主要是提升併發的性能以及更天然,由於每一個document都只會在某一個主分片中,因此由所在主分片分配序列號比由以前經過一個參數_version,至關於由整個ES集羣分配版本號要來的更好。
To ensure an older version of a document doesn’t overwrite a newer version, every operation performed to a document is assigned a sequence number by the primary shard that coordinates that change. The sequence number is increased with each operation and thus newer operations are guaranteed to have a higher sequence number than older operations. Elasticsearch can then use the sequence number of operations to make sure a newer document version is never overridden by a change that has a smaller sequence number assigned to it.
簡單翻譯就是爲確保較舊版本的文檔不會覆蓋較新版本,對文檔執行的每一個操做都會由協調該更改的主分片分配序列號。每次操做都會增長序列號,所以保證較新的操做具備比舊操做更高的序列號。而後,Elasticsearch可使用序列號操做來確保更新的文檔版本永遠不會被分配給它的序列號更小的更改覆蓋。
具體的實戰就不作了,本質思想也很簡單,就是版本號是存儲在本身的數據庫中的,能夠由開發人員本身控制。可是在6.7版本以後,就移除這個功能,主要是由於:The update API does not support versioning other than internalExternal (version types external and external_gte) or forced (version type force) versioning is not supported by the update API as it would result in Elasticsearch version numbers being out of sync with the external system. 更新API不支持外部(版本類型external和external_gte)或強制(版本類型force)版本控制,由於它會致使Elasticsearch版本號與外部系統不一樣步