ES:基於_version進行樂觀鎖併發控制
圖示的衝突過程,其實就是es的併發衝突問題,會導致資料不準確
當併發操作es的執行緒越多,或者讀取一份資料,供使用者查詢和操作的時間越長,在這段時間裡,如果資料被其他使用者修改,那麼我們拿到的就是舊資料,基於舊資料去操作,就會導致錯誤的結果
1、悲觀鎖與樂觀鎖兩種併發控制方案
悲觀鎖(Pessimistic Lock),,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人想拿這個資料就會block直到它拿到鎖。傳統的關係型資料庫裡邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖
樂觀鎖(Optimistic Lock), 每次去拿資料的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個資料,可以使用版本號等機制。樂觀鎖適用於多讀的應用型別,這樣可以提高吞吐量,像資料庫如果提供類似於write_condition機制的其實都是提供的樂觀鎖。
悲觀鎖的優點是:方便,直接加鎖,對應用程式來說,透明,不需要做額外的操作;缺點,併發能力很低,同一時間只能一條執行緒操作資料
樂觀鎖的優點是:併發能力很高,不給資料加鎖,大量執行緒併發操作;缺點,麻煩,每次更新的時候,都要先對比版本號,然後可能需要重新載入資料,再次修改,再寫;這個過程,可能要重複好幾次。
2、內部如何基於_version進行樂觀鎖併發控制
(1)_version元資料
PUT /test_index/test_type/5
{
"test_field": "test test"
}
{ "_index": "test_index", "_type": "test_type", "_id": "5", "_version": 1, "result": "created", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "created": true }
第一次建立一個document的時候,它的_version內部版本號就是1;以後,每次對這個document執行修改或者刪除操作,都會對這個_version版本號自動加1;哪怕是刪除,也會對這條資料的版本號加1
DELETE /test_index/test_type/5
{ "found": true, "_index": "test_index", "_type": "test_type", "_id": "5", "_version": 3, "result": "deleted", "_shards": { "total": 2, "successful": 1, "failed": 0 } }
在刪除一個document之後,可以從一個側面證明,它不是立即物理刪除掉的,因為它的一些版本號等資訊還是保留著的。先刪除一條document,再重新建立這條document,其實會在delete version基礎之上,再把version號加1
(2)圖解內部如何基於_version進行樂觀鎖併發控制
(3)基於external version進行樂觀鎖併發控制
語法:
?version=1&version_type=external
version_type=external,唯一的區別在於。
_version,只有當你提供的version與es中的_version一模一樣的時候,才可以進行修改,只要不一樣,就報錯
version_type=external,只有當你提供的version比es中的_version大的時候,才能完成修改。
PUT /test_index/test_type/8?version=2&version_type=external
{
"test_field": "test test1"
}
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[test_type][8]: version conflict, current version [2] is higher or equal to the one provided [2]",
"index_uuid": "toqtg_FpS-e8bCUkqRr2-Q",
"shard": "1",
"index": "test_index"
}
],
"type": "version_conflict_engine_exception",
"reason": "[test_type][8]: version conflict, current version [2] is higher or equal to the one provided [2]",
"index_uuid": "toqtg_FpS-e8bCUkqRr2-Q",
"shard": "1",
"index": "test_index"
},
"status": 409
}
重新基於最新的版本號發起更新
GET /test_index/test_type/8
{
"_index": "test_index",
"_type": "test_type",
"_id": "8",
"_version": 3,
"found": true,
"_source": {
"test_field": "test test1"
}
}
PUT /test_index/test_type/8?version=3&version_type=external
{
"test_field": "test test1"
}
{
"_index": "test_index",
"_type": "test_type",
"_id": "8",
"_version": 3,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}