1. 程式人生 > >ES:基於_version進行樂觀鎖併發控制

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
}