[MySQL] 行級鎖SELECT ... LOCK IN SHARE MODE 和 SELECT ... FOR UPDATE

1、譯文

翻譯來自官方文檔:Locking Readshtml

If you query data and then insert or update related data within the same transaction, the regular SELECT statement does not give enough protection. Other transactions can update or delete the same rows you just queried. InnoDB supports two types of locking reads that offer extra safety:mysql

若是你在查詢數據,而後在同一個事務裏插入或者修改相關的數據,常規的 select 語句不會提供足夠的保護。其餘的事務能夠修改或者刪除你正在查詢的行。InnoDB 支持兩種能夠提供安全機制的讀取鎖:sql

SELECT … LOCK IN SHARE MODE sets a shared mode lock on any rows that are read. Other sessions can read the rows, but cannot modify them until your transaction commits. If any of these rows were changed by another transaction that has not yet committed, your query waits until that transaction ends and then uses the latest values.安全

SELECT … LOCK IN SHARE MODE 在讀取的行上設置一個共享鎖,其餘的session能夠讀這些行,但在你的事務提交以前不能夠修改它們。若是這些行裏有被其餘的尚未提交的事務修改,你的查詢會等到那個事務結束以後使用最新的值。session

For index records the search encounters, SELECT … FOR UPDATE locks the rows and any associated index entries, the same as if you issued an UPDATE statement for those rows. Other transactions are blocked from updating those rows, from doing SELECT … LOCK IN SHARE MODE, or from reading the data in certain transaction isolation levels. Consistent reads ignore any locks set on the records that exist in the read view. (Old versions of a record cannot be locked; they are reconstructed by applying undo logs on an in-memory copy of the record.)app

索引搜索遇到的記錄,SELECT … FOR UPDATE 會鎖住行及任何關聯的索引條目,和你對那些行執行 update 語句相同。其餘的事務會被阻塞在對這些行執行 update 操做,獲取共享鎖,或從某些事務隔離級別讀取數據等操做。一致性讀(Consistent Nonlocking Reads)會忽略在讀取視圖上的記錄的任何鎖。(舊版本的記錄不能被鎖定;它們經過應用撤銷日誌在記錄的內存副本上時被重建。)ui

All locks set by LOCK IN SHARE MODE and FOR UPDATE queries are released when the transaction is committed or rolled back.spa

Note 
Locking of rows for update using SELECT FOR UPDATE only applies when autocommit is disabled (either by beginning transaction with START TRANSACTION or by setting autocommit to 0. If autocommit is enabled, the rows matching the specification are not locked.翻譯

全部被共享鎖和排他鎖查詢所設置的鎖都會在事務提交或者回滾以後被釋放。3d

注: 
使用 SELECT FOR UPDATE 爲 update 操做鎖定行,只適用於 autocommit 被禁用(當使用 START TRANSACTION 開始事務或者設置 autocommit 爲0時)。若是 autocommit 已啓用,符合規範的行不會被鎖定。

2、總結

此處參考:MySQL中的共享鎖與排他鎖

SELECT … LOCK IN SHARE MODE :共享鎖(S鎖, share locks)。其餘事務能夠讀取數據,但不能對該數據進行修改,直到全部的共享鎖被釋放。

若是事務對某行數據加上共享鎖以後,可進行讀寫操做;其餘事務能夠對該數據加共享鎖,但不能加排他鎖,且只能讀數據,不能修改數據。

SELECT … FOR UPDATE:排他鎖(X鎖, exclusive locks)。若是事務對數據加上排他鎖以後,則其餘事務不能對該數據加任何的鎖。獲取排他鎖的事務既能讀取數據,也能修改數據。

注:普通 select 語句默認不加鎖,而CUD操做默認加排他鎖。

3、驗證

注:使用 mysql 版本爲 5.7.9,事務隔離級別爲InnoDB默認隔離級別 可重複讀(Repeated Read)。

對如下幾種狀況進行驗證:

  1. 當前事務獲取共享鎖後,能夠讀寫,其餘事務是否能夠進行讀寫操做和獲取共享鎖;
  2. 兩個事務同時獲取共享鎖後,是否能夠進行update操做;
  3. 當前事務獲取排他鎖後,其餘事務是否能夠進行讀寫操做和獲取共享鎖;
  4. 是否可對一條數據加多個排他鎖;
  5. 行鎖和索引的關係;
  6. 索引數據重複率過高會致使全表掃描;

一、當前事務獲取共享鎖後,能夠讀寫,其餘事務是否能夠進行讀寫操做和獲取共享鎖:能夠讀,能夠獲取共享鎖,不能夠寫

當前事務能夠寫: 
這裏寫圖片描述

事務1獲取某行數據共享鎖後,事務2更新該行阻塞: 
這裏寫圖片描述

事務1提交以後,事務2更新成功: 
這裏寫圖片描述

二、兩個事務同時獲取某行數據共享鎖後,是否能夠進行update操做:不能夠

兩個事務同時獲取某行數據共享鎖,事務1更新該行阻塞: 
這裏寫圖片描述

事務2提交以後,事務1更新成功: 
這裏寫圖片描述

三、當前事務獲取某行數據排他鎖後,其餘事務是否能夠對該行數據進行讀寫操做和獲取共享鎖:其餘事務能夠讀,不能夠獲取共享鎖,不能夠寫

能夠讀該行數據: 
這裏寫圖片描述

不能夠獲取該行數據共享鎖: 
這裏寫圖片描述

不能夠更新該行數據: 
這裏寫圖片描述

四、是否可對一條數據加多個排他鎖:不能夠 
這裏寫圖片描述

五、行鎖和索引的關係:查詢字段未加索引(主鍵索引、普通索引等)時,使用表鎖

注:InnoDB行級鎖基於索引實現。

未加索引時,兩種行鎖狀況爲(使用表鎖): 
- 事務1獲取某行數據共享鎖,其餘事務能夠獲取不一樣行數據的共享鎖,不能夠獲取不一樣行數據的排他鎖 
- 事務1獲取某行數據排他鎖,其餘事務不能夠獲取不一樣行數據的共享鎖、排他鎖

加索引後,兩種行鎖爲(使用行鎖):

  • 事務1獲取某行數據共享鎖,其餘事務能夠獲取不一樣行數據的排他鎖
  • 事務1獲取某行數據排他鎖,其餘事務能夠獲取不一樣行數據的共享鎖、排他鎖

未加索引表結構: 
這裏寫圖片描述

未加索引,事務1獲取某行數據共享鎖,事務2獲取不一樣行數據共享鎖成功: 
這裏寫圖片描述

未加索引,事務1獲取某行數據共享鎖,事務2更新不一樣行數據阻塞: 
這裏寫圖片描述

未加索引,事務1獲取某行數據排他鎖,事務2獲取不一樣行數據共享鎖阻塞: 
這裏寫圖片描述

未加索引,事務1獲取某行數據排他鎖,事務2獲取不一樣行數據排他鎖阻塞: 
這裏寫圖片描述

加索引後表結構: 
這裏寫圖片描述

加索引後,事務1獲取某行數據共享鎖,事務2更新不一樣行數據成功: 
這裏寫圖片描述

加索引後,事務1獲取某行數據排他鎖,事務2獲取不一樣行數據共享鎖成功: 
這裏寫圖片描述

加索引後,事務1獲取某行數據排他鎖,事務2獲取不一樣行數據排他鎖成功: 
這裏寫圖片描述

六、索引數據重複率過高會致使全表掃描:當表中索引字段數據重複率過高,則MySQL可能會忽略索引,進行全表掃描,此時使用表鎖。可以使用 force index 強制使用索引。

表結構:

CREATE TABLE `room` ( `id` int(11) NOT NULL AUTO_INCREMENT, `uid` int(11) NOT NULL, `username` varchar(200) NOT NULL DEFAULT '', `state` varchar(255) NOT NULL DEFAULT '-1', `created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `index_uid` (`uid`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

表數據: 
這裏寫圖片描述

獲取 uid = ‘11’ 的數據的行鎖並更新,但更新失敗: 
這裏寫圖片描述

下降數據重複率,更新成功: 
這裏寫圖片描述

這裏寫圖片描述

強制使用索引,更新成功:force index(index_uid) 
這裏寫圖片描述

注:此處若是使用表鎖,爲什麼其餘事務能夠獲取排他鎖?

答:因爲使用 force index ,而 InnoDB 行級鎖基於索引實現,所以此處使用的是行鎖。

4、InnoDB 行鎖類型簡介

參考:Mysql中那些鎖機制之InnoDB

InnoDB行鎖的三種類型:

    • Record Lock:對索引項加鎖,鎖定符合條件的行。其餘事務不能修改和刪除加鎖項;
    • Gap Lock:對索引項之間的「間隙」加鎖,鎖定記錄的範圍,不包含索引項自己。其餘事務不能再鎖範圍內插入數據;
    • Next-key Lock: 鎖定索引項自己和索引範圍,即Record Lock 和 Gap Lock 的結合。可解決幻讀問題。
相關文章
相關標籤/搜索