Elasticsearch由淺入深(四)ES併發衝突、悲觀鎖與樂觀鎖、_version樂觀鎖併發

ES併發衝突

舉個例子,好比是電商場景下,假設說,咱們有個程序,工做的流程是這樣子的:緩存

  1. 讀取商品信息(包含了商品庫存)
  2. 用戶下單購買
  3. 更新商品信息(主要是將庫存減1)

咱們好比我們的程序就是多線程的,因此可能有多個線程併發的去執行上述的3步驟流程多線程

有一個牙膏,庫存100件,如今,同時有兩我的都過來讀取了牙育的數據,而後下單購買了這管牙膏,此時兩個線程併發的服務於兩我的,同時在進行商品庫存數據的修改併發

總有一個線程是先到的,假設就是線程A ,此時線程A就會先將牙育的庫存設置爲99件,而後線程B再次將牙育的庫存設置爲99件,此時結果就已經錯了異步

正確的狀況下,咱們指望的,應該是說,線程A將庫存-1,設置爲99件;而後線程B接着這個99件,將庫存-1,變爲98件,而後設置到ES中。最終ES中應該庫存是98件纔對啊.spa

普通的ES操做流程:線程

  1. 先get document數據,商品信息,顯示到網頁上,同時在內存中緩存該document的數據
  2. 當網頁發生了購買以後,直接基於內存中的數據,進行計算和操做
  3. 將計算後的結果寫回ES中

上面說的這個流程和過程,其實就是ES中的併發衝突問題,會致使數據不許確:版本控制

  1. 有些場景下,實際上是無所謂的,不care這個數據不正確的事情,好比說,咱們若是就只是簡單的將數據寫入ES ,不管數據是什麼樣的,均可以;還有些狀況下,即便是算錯了,也能夠 
  2. 當併發操做ES的線程越多,或者併發請求越多;或者是讀取一份數據,供用戶查閱和操做的時間越長,由於這段時間裏極可能數據在ES中已經被修改了,那麼咱們拿到的就是舊數據,基於 1舊數據去操做,後面結果確定就錯了

悲觀鎖與樂觀鎖

優缺點:code

  • 悲觀鎖
    優勢:方便,直接加鎖,對應用程序來講,透明,不須要作額外的操做;
    缺點:併發能力很低,同一時間只能有一條線程操做數據,
  • 樂觀鎖
    優勢:併發能力很高,不給數據加鎖,大量線程併發操做;
    缺點:麻煩,每次更新的時候,都要先比對版本號,而後可能須要從新加載數據,再次修改,再寫;這個過程,可能要重複好幾回

悲觀鎖併發控制機制

悲觀鎖併發控制方案,就是在各類狀況下,都上鎖。上鎖以後,就只有一個線程能夠操做這一條數據了,固然,不一樣的場景下,上的敏不一樣,行級鎖,表級鎖,讀鎖,寫鎖。blog

樂觀鎖併發控制機制

樂觀鎖是不加鎖的, ,每一個線程均可以任意操做內存

線程B去判斷,當前數據的版本號,version=1,與es中的數據的版本號, version=2,是否相同?明顯是不一樣的。版本號不一樣,說明數據已經被其餘人修改過了。此時用戶B不會用99去更新。而是從新去es中讀取最新的數據版本,99件,再次減 1,變爲98件,再執行上述流程,就能夠寫入

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

示例數據:

PUT /test_index/test_type/6
{
  "test_field": "test test"
}
{
  "_index": "test_index",
  "_type": "test_type",
  "_id": "6",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "created": true
}

第一次建立一個document的時候,它的_version內部版本號就是1;之後,每次對這個document執行修改或者刪除操做,都會對這個_version版本號自動加1;哪怕是刪除,也會對這條數據的版本號加1

 

{
  "found": true,
  "_index": "test_index",
  "_type": "test_type",
  "_id": "6",
  "_version": 4,
  "result": "deleted",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  }
}

咱們會發現,在刪除一個document以後,能夠從一個側面證實,它不是當即物理刪除掉的,由於它的一些版本號等信息仍是保留着的。先刪除一條document,再從新建立這條document,其實會在delete version基礎之上,再把version號加1

ES後臺同步:知識點: es的後臺,不少的這種相似於replica同步請求,都是多線程異步的,也就是說,多個修改請求之間,是亂序的,沒有順序的,可能後修改的先到,先修改的後到

假設es內部沒有樂觀鎖併發控制機制:

  1. 後修改的先到了,此時version=2, field-test3
  2. 而後先修改的後到了,此時若是不基於version進行版本控制,直接將field-test2蓋過去,此時數據就錯了
  3. 由於按照順序來講,應該是數據從field=test1 ,先變爲 frield-test2,再變爲field-test3

es內部的多線程異步併發修改時,是基於本身的_version版本號進行,在後修改先到時,那麼field-test3, version=2,先修改後到時,先修改的field=test2 , version-1 ,此時會比較一下version號,是否相等,若是不相等的話,那麼就直接將 field-test2這條數據給扔掉,就不須要了,這樣的話,結果就會保持爲一個正確的狀態, field-test3

相關文章
相關標籤/搜索