MySQL中的隔離級別和悲觀鎖及樂觀鎖示例

MySQL的事務支持
MyISAM:不支持事務,用於只讀程序提高性能
InnoDB:支持ACID事務、行級鎖、併發

隔離級別(主要對於InnoDB而言)
隔離級別決定了一個session中的事務可能對另一個session的影響、併發session對數據庫的操作、一個session中所見數據的一致性
ANSI標準定義了4個隔離級別,MySQL的InnoDB都支持:

1、READ UNCOMMITTED:最低級別的隔離,通常又稱爲dirty read,它允許一個事務讀取還沒commit的數據,這樣可能會提高性能,但是dirty read可能不是我們想要的   
2READ COMMITTED:在一個事務中只允許已經commit的記錄可見,如果sessionselect還在查詢中,另一session此時insert一條記錄,則新添加的數據不可見   
3、REPEATABLE READ:在一個事務開始後,其他session對數據庫的修改在本事務中不可見,直到本事務commitrollback。在一個事務中重複select的結果一樣,除非本事務中update數據庫。   
4、SERIALIZABLE:最高級別的隔離,只允許事務串行執行。爲了達到此目的,數據庫會鎖住每行已經讀取的記錄,其他session不能修改數據直到前一事務結束,事務commit或取消時才釋放鎖。 

可以使用如下語句設置MySQL的session隔離級別:

SET TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}

MySQL默認的隔離級別是REPEATABLE READ,在設置隔離級別爲READ UNCOMMITTED或SERIALIZABLE時要小心,READ UNCOMMITTED會導致數據完整性的嚴重問題,而SERIALIZABLE會導致性能問題並增加死鎖的機率

樂觀鎖、悲觀鎖
悲觀鎖:在讀取數據時鎖住那幾行,其他對這幾行的更新需要等到悲觀鎖結束時才能繼續
樂觀所:讀取數據時不鎖,更新時檢查是否數據已經被更新過,如果是則取消當前更新
一般在悲觀鎖的等待時間過長而不能接受時我們纔會選擇樂觀鎖

悲觀鎖
要使用悲觀鎖,我們必須關閉mysql數據庫的自動提交屬性,因爲MySQL默認使用autocommit模式,也就是說,當你執行一個更新操作後,MySQL會立刻將結果進行提交。

我們可以使用命令設置MySQL爲非autocommit模式:
set autocommit=0;
# set autocommit=0;
begin;
#查詢出商品信息
select status from t_goods where id=1 for update;
#根據商品信息生成訂單
insert into t_orders (id,goods_id) values (null,1);
#修改商品status爲2
update t_goods set status=2;
#提交事務
COMMIT;

注:上面的begin/commit爲事務的開始和結束,因爲在前一步我們關閉了mysql的autocommit,所以需要手動控制事務的提交,在這裏就不細表了。

與普通查詢不一樣的是,我們使用了select…for update的方式,這樣就通過數據庫實現了悲觀鎖。此時在t_goods表中,id爲1的 那條數據就被我們鎖定了,其它的事務必須等本次事務提交之後才能執行。這樣我們可以保證當前的數據不會被其它事務修改。

注:需要注意的是,在事務中,只有SELECT … FOR UPDATE 或LOCK IN SHARE MODE 同一筆數據時會等待其它事務結束後才執行,一般SELECT … 則不受此影響。

補充:MySQL select…for update的Row Lock與Table Lock
上面我們提到,使用select…for update會把數據給鎖住,不過我們需要注意一些鎖的級別,MySQL InnoDB默認Row-Level Lock,所以只有「明確」地指定主鍵,或者「明確」的指定了索引字段,MySQL 纔會執行Row lock (只鎖住被選取的數據) ,否則MySQL 將會執行Table Lock (將整個數據表單給鎖住)。

樂觀鎖
樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設認爲數據一般情況下不會造成衝突,所以在數據進行提交更新的時候,纔會正式對數據的衝突與否進行檢測,如果發現衝突了,則讓返回用戶錯誤的信息,讓用戶決定如何去做。那麼我們如何實現樂觀鎖呢,一般來說有以下2種方式:

1.使用數據版本(Version)記錄機制實現,這是樂觀鎖最常用的一種實現方式。何謂數據版本?即爲數據增加一個版本標識,一般是通過爲數據庫表增加一個數字類型的 “version” 字段來實現。當讀取數據時,將version字段的值一同讀出,數據每更新一次,對此version值加一。當我們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的version值進行比對,如果數據庫表當前版本號與第一次取出來的version值相等,則予以更新,否則認爲是過期數據。用下面的一張圖來說明:

2.樂觀鎖定的第二種實現方式和第一種差不多,同樣是在需要樂觀鎖控制的table中增加一個字段,名稱無所謂,字段類型使用時間戳(timestamp), 和上面的version類似,也是在更新提交的時候檢查當前數據庫中數據的時間戳和自己更新前取到的時間戳進行對比,如果一致則OK,否則就是版本衝突。

下單操作包括3步驟:
1.查詢出商品信息
select (status,status,version) from t_goods where id=#{id}
2.根據商品信息生成訂單
3.修改商品status爲2
update t_goods 
set status=2,version=version+1
where id=#{id} and version=#{version};

樂觀鎖可以看成MVCC的一種實現

發佈了70 篇原創文章 · 獲贊 7 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章