Elasticsearch技術解析與實戰(六)Elasticsearch併發

hellozhxy發表於2018-04-12

樂觀鎖與悲觀鎖

    圖示的衝突過程,其實就是es的併發衝突問題,會導致資料不準確

    當併發操作es的執行緒越多,或者讀取一份資料,供使用者查詢和操作的時間越長,在這段時間裡,如果資料被其他使用者修改,那麼我們拿到的就是舊資料,基於舊資料去操作,就會導致錯誤的結果

 

  • 悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人想拿這個資料就會block直到它拿到鎖。傳統的關係型資料庫裡邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。
  • 樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿資料的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個資料,可以使用版本號等機制。樂觀鎖適用於多讀的應用型別,這樣可以提高吞吐量,像資料庫如果提供類似於write_condition機制的其實都是提供的樂觀鎖。
  • 兩者之間的優缺點

    悲觀併發控制實際上是“先取鎖再訪問”的保守策略,為資料處理的安全提供了保證。但是併發效率很低,同一時間只能有一條執行緒運算元據
    悲觀鎖併發能力很高,不給資料加鎖,大量執行緒併發操作,但是每次更新的時候,都要先比對版本號,然後可能需要更新資料,再次修改,再寫

Elasticsearch內部基於_version進行樂觀鎖併發控制

1.構造一條資料出來

PUT /test_index/test_type/7
{
  "test_field": "test test"
}

2.模擬兩個客戶端,都獲取到了同一條資料,其中一個客戶端,先更新了一下這個資料

  同時帶上資料的版本號,確保說,es中的資料的版本號,跟客戶端中的資料的版本號是相同的,才能修改

PUT /test_index/test_type/7?version=1 
{
  "test_field": "test client 1"
}

3.另外一個客戶端,嘗試基於version=1的資料去進行修改,同樣帶上version版本號,進行樂觀鎖的併發控制

PUT /test_index/test_type/7?version=1 
{
  "test_field": "test client 2"
}

4.基於最新的資料和版本號,去進行修改,修改後,帶上最新的版本號,可能這個步驟會需要反覆執行好幾次,才能成功,特別是在多執行緒併發更新同一條資料很頻繁的情況下

PUT /test_index/test_type/7?version=2 
{
  "test_field": "test client 2"
}

 

 基於external version進行樂觀鎖併發控制

  es提供了一個新特性,就是說,你可以不用基於它提供的內部_version版本號進行併發控制,可以基於自己維護的一個版本號來進行併發控制。

  引數寫法
  ?version=1&version_type=external

  ?version=1和?version=1&version_type=external的區別

  _version,只有當你提供的version與es中的_version一模一樣的時候,才可以進行修改,只要不一樣,就報錯;

  當version_type=external的時候,只有當你提供的version比es中的_version大的時候,才能完成修改

1.先構造一條資料

PUT /test_index/test_type/8
{
  "test_field": "test"
}

2.模擬兩個客戶端同時查詢到這條資料,第一個客戶端先進行修改,此時客戶端程式是在自己的資料庫中獲取到了這條資料的最新版本號,比如說是2

PUT /test_index/test_type/8?version=2&version_type=external
{
  "test_field": "test client 1"
}

3.模擬第二個客戶端,同時拿到了自己資料庫中維護的那個版本號,也是2,同時基於version=2發起了修改

PUT /test_index/test_type/8?version=2&version_type=external
{
  "test_field": "test client 2"
}

4.在併發控制成功後,重新基於最新的版本號發起更新

PUT /test_index/test_type/8?version=3&version_type=external
{
  "test_field": "test client 2"
}

 

相關文章