mysql悲觀鎖與樂觀鎖

簡介

   數據庫管理系統(DBMS)中的併發控制的任務是確保在多個事務同時存取數據庫中同一數據時不破壞事務的隔離性和統一性以及數據庫的統一性。mysql

用途sql

   樂觀鎖和悲觀鎖是併發控制主要採用的技術手段。不管是悲觀鎖仍是樂觀鎖,都是人們定義出來的概念,能夠認爲是一種思想。其實不只僅是數據庫系統中有樂觀鎖和悲觀鎖的概念,像 memcache、hibernate、tair 等都有相似的概念。針對於不一樣的業務場景,應該選用不一樣的併發控制方式。因此,不要把樂觀併發控制和悲觀併發控制狹義的理解爲 DBMS 中的概念,更不要把他們和數據中提供的鎖機制(行鎖、表鎖、排他鎖、共享鎖)混爲一談。其實,在 DBMS 中,悲觀鎖正是利用數據庫自己提供的鎖機制來實現的。數據庫

 

悲觀鎖

     在關係數據庫管理系統裏,悲觀併發控制(又名 「悲觀鎖」,Pessimistic Concurrency Control,縮寫 「PCC」)是一種併發控制的方法。它能夠阻止一個事務以影響其餘用戶的方式來修改數據。若是一個事務執行的操做都某行數據應用了鎖,那只有當這個事務把鎖釋放,其餘事務纔可以執行與該鎖衝突的操做。悲觀併發控制主要用於數據爭用激烈的環境,以及發生併發衝突時使用鎖保護數據的成本要低於回滾事務的成本的環境中。安全

    悲觀鎖,正如其名,它指的是對數據被外界(包括本系統當前的其餘事務,以及來自外部系統的事務處理)修改持保守態度 (悲觀),所以,在整個數據處理過程當中,將數據處於鎖定狀態。 悲觀鎖的實現,每每依靠數據庫提供的鎖機制 (也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,不然,即便在本系統中實現了加鎖機制,也沒法保證外部系統不會修改數據)。併發

    在數據庫中,悲觀鎖的流程以下:
在對任意記錄進行修改前,先嚐試爲該記錄加上排他鎖(exclusive locking)。
若是加鎖失敗,說明該記錄正在被修改,那麼當前查詢可能要等待或者拋出異常。 具體響應方式由開發者根據實際須要決定。
若是成功加鎖,那麼就能夠對記錄作修改,事務完成後就會解鎖了。
其間若是有其餘對該記錄作修改或加排他鎖的操做,都會等待咱們解鎖或直接拋出異常。spa

 

MySQL InnoDB 中使用悲觀鎖hibernate

要使用悲觀鎖,咱們必須關閉 mysql 數據庫的自動提交屬性,由於 MySQL 默認使用 autocommit 模式,也就是說,當你執行一個更新操做後,MySQL 會馬上將結果進行提交。 set autocommit=0;設計

//1.開始事務 begin;/begin work;/start transaction; (三者選一就能夠) //2.查詢出商品信息 select status from t_goods where id=1 for update; //3.根據商品信息生成訂單 insert into t_orders (id,goods_id) values (null,1); //4.修改商品status爲2 update t_goods set status=2; //5.提交事務 commit;/commit work; 

       上面的查詢語句中,咱們使用了 select…for update 的方式,這樣就經過開啓排他鎖的方式實現了悲觀鎖。此時在 t_goods 表中,id 爲 1 的 那條數據就被咱們鎖定了,其它的事務必須等本次事務提交以後才能執行。這樣咱們能夠保證當前的數據不會被其它事務修改。code

       上面咱們提到,使用 select…for update 會把數據給鎖住,不過咱們須要注意一些鎖的級別,MySQL InnoDB 默認行級鎖。行級鎖都是基於索引的,若是一條 SQL 語句用不到索引是不會使用行級鎖的,會使用表級鎖把整張表鎖住,這點須要注意。索引

優勢與不足展開目錄

       悲觀併發控制其實是 「先取鎖再訪問」 的保守策略,爲數據處理的安全提供了保證。可是在效率方面,處理加鎖的機制會讓數據庫產生額外的開銷,還有增長產生死鎖的機會;另外,在只讀型事務處理中因爲不會產生衝突,也不必使用鎖,這樣作只能增長系統負載;還有會下降了並行性,一個事務若是鎖定了某行數據,其餘事務就必須等待該事務處理完才能夠處理那行數。

 

樂觀鎖

       在關係數據庫管理系統裏,樂觀併發控制(又名 「樂觀鎖」,Optimistic Concurrency Control,縮寫 「OCC」)是一種併發控制的方法。它假設多用戶併發的事務在處理時不會彼此互相影響,各事務可以在不產生鎖的狀況下處理各自影響的那部分數據。在提交數據更新以前,每一個事務會先檢查在該事務讀取數據後,有沒有其餘事務又修改了該數據。若是其餘事務有更新的話,正在提交的事務會進行回滾。樂觀事務控制最先是由孔祥重(H.T.Kung)教授提出。

樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設認爲數據通常狀況下不會形成衝突,因此在數據進行提交更新的時候,纔會正式對數據的衝突與否進行檢測,若是發現衝突了,則讓返回用戶錯誤的信息,讓用戶決定如何去作。

相對於悲觀鎖,在對數據庫進行處理的時候,樂觀鎖並不會使用數據庫提供的鎖機制。通常的實現樂觀鎖的方式就是記錄數據版本。

數據版本, 爲數據增長的一個版本標識。當讀取數據時,將版本標識的值一同讀出,數據每更新一次,同時對版本標識進行更新。當咱們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的版本標識進行比對,若是數據庫表當前版本號與第一次取出來的版本標識值相等,則予以更新,不然認爲是過時數據。

使用版本號實現樂觀鎖

     使用數據版本(Version)記錄機制實現,這是樂觀鎖最經常使用的一種實現方式。何謂數據版本?即爲數據增長一個版本標識,通常是經過爲數據庫表增長一個數字類型的 「version」 字段來實現。當讀取數據時,將 version 字段的值一同讀出,數據每更新一次,對此 version 值加一。當咱們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的 version 值進行比對,若是數據庫表當前版本號與第一次取出來的 version 值相等,則予以更新,不然認爲是過時數據

示例

  • 數據庫表設計
    task 表有三個字段,分別是 id,value、version
  • 實現

1. 先讀 task 表的數據(實際上這個表只有一條記錄),獲得 version 的值爲 versionValue
2. 每次更新 task 表中的 value 字段時,爲了防止發生衝突,須要這樣操做

update task set value = newValue,version = versionValue + 1 where version = versionValue; 

只有這條語句執行了,才代表本次更新 value 字段的值成功

如假設有兩個節點 A 和 B 都要更新 task 表中的 value 字段值,差很少在同一時刻,A 節點和 B 節點從 task 表中讀到的 version 值爲 2,那麼 A 節點和 B 節點在更新 value 字段值的時候,都操做

update task set value = newValue,version = 3 where version = 2;

實際上只有 1 個節點執行該 SQL 語句成功,假設 A 節點執行成功,那麼此時 task 表的 version 字段的值是 3,B 節點再操做

update task set value = newValue,version = 3 where version = 2;

這條 SQL 語句是不執行的,這樣就保證了更新 task 表時不發生衝突。

相關文章
相關標籤/搜索