
圖示的沖突過程,其實就是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
}