一文了解InnoDB事務鎖

鎖機制用於管理對共享資源的併發訪問,而數據庫自己做爲共享資源的集合,內部須要提供必定的鎖機制來保證事務的隔離性。本文探討的是MySQL(5.7)InnoDB引擎下的鎖機制。html

鎖類型

共享鎖

共享鎖也稱爲讀鎖,容許事務讀一行數據。共享鎖之間是兼容的,也就是說多個事務能夠針對同一行數據加共享鎖。mysql

排他鎖

共享鎖也稱爲寫鎖,容許事務刪除或更新一行數據。排他鎖之間以及排他鎖和共享鎖是不兼容的。sql

表級鎖

表鎖(table lock)對整個表加鎖,影響表的全部記錄數據庫

  • 共享鎖 LOCK TABLE table_name READ; 用讀鎖鎖表,會阻塞其餘事務修改表數據。
  • 排他鎖 LOCK TABLE table_name WRITE; 用寫鎖鎖表,會阻塞其餘事務讀和寫。

行級鎖

行鎖(row lock)併發

  • 鎖定相應記錄,但不影響其餘記錄
  • 實際針對記錄的索引加鎖,而非記錄自己
  • 若是表中沒有索引,則會使用隱式索引進行鎖定

共享鎖app

  • 容許一個事務去讀一行,阻止其餘事務得到相同數據集的排他鎖。若事務T對數據對象A加上S鎖,則事務T能夠讀A但不能修改A,其餘事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖。這保證了其餘事務能夠讀A,但在T釋放A上的S鎖以前不能對A作任何修改。
    • 能夠經過select … lock in share mode顯式獲取共享鎖

排他鎖性能

  • 又稱寫鎖。容許獲取排他鎖的事務更新和刪除數據,阻止其餘事務取得相同的數據集共享讀鎖和排他寫鎖。若事務T對數據對象A加上X鎖,事務T能夠讀A也能夠修改A,其餘事務不能再對A加任何鎖,直到T釋放A上的鎖。
  • 能夠經過select …for update顯式獲取排他鎖

意向鎖

由於表鎖覆蓋了行鎖的數據,因此表鎖和行鎖也會產生衝突,爲了方便檢測表級鎖和行級鎖之間的衝突,就引入了意向鎖。spa

  1. 意向鎖分爲意向讀鎖(IS)意向寫鎖(IX)。
  2. 意向鎖是表級鎖,可是卻表示事務要鎖定或者將要鎖定某行記錄,而不是整個表。
  3. 意向鎖之間不會產生衝突,真正的衝突在加行鎖時檢查。
  4. 在給一行記錄加鎖前,首先要給該表加意向鎖,也就是要同時加表意向鎖和行鎖。
  5. 意向鎖不會阻塞除全表掃(如LOCK TABLE WRITE)之外的任何請求。

表級鎖兼容矩陣以下:code

Table-level lock type compatibility is summarized in the following matrix.orm

Gap Lock

間隙鎖(Gap Lock)是鎖定索引記錄之間的間隙,鎖定在第一個以前或最後一個索引記錄以後的間隙上。

能夠經過如下兩種方式顯式關閉Gap Lock

  • 將事務隔離級別設置爲READ COMMITTED
  • 將參數innodb_locks_unsafe_for_binlog設置爲1 (已棄用)

Next-Key Lock

Record Lock + Gap Lock 的結合

Auto-INC Lock

  • 表級鎖
  • AUTO_INCREMENT字段
  • innodb_autoinc_lock_mode
    • 0=傳統(執行插入語句時,獲取表級鎖,語句執行完畢釋放)
    • 1=連續(對於能提早估算數量的插入語句,經過互斥量mutex來鎖定分配Id操做,對於其餘複雜插入則採用表級鎖方式)
    • 2=交叉(對全部insert都採用互斥量mutex鎖定,性能較好,可是會形成ID不連續;另外binlog的format須爲row)

加鎖規則

在InnoDB的默認的可重複讀(REPEATABLE-READ)隔離級別下,存在如下的加鎖規則:

  • 非鎖定讀(普通select):不會產生鎖,也不會被鎖定
  • Insert(主鍵自增):存在 Auto-INC Lock、 insert intention gap lock、行級排他鎖
  • 外鍵值的插入和更新:採用select lock in share mode方式對父表記錄加共享鎖
  • 鎖定讀:命中惟一索引產生行鎖;普通索引產生Next-Key Lock;沒有索引則產生表鎖
  • Update:同上
  • Delete:同上

Online DDL 修改表結構

  • 增長、刪除字段:不鎖表
  • 添加、刪除索引:不鎖表
  • 重命名字段:不鎖表
  • 更改字段類型:鎖表

Online DDL and pt-online-schema-change for some alter operations applied on a table contains 1,078,880 rows

鎖問題

隔離級別 髒 讀 不可重複讀 幻 讀
未提交讀(Read uncommitted) 可能 可能 可能
已提交讀(Read committed) 不可能 可能 可能
可重複讀(Repeatable read) 不可能 不可能 可能
可串行化(Serializable ) 不可能 不可能 不可能

丟失更新

丟失更新分爲兩個層面

  • 一個層面數據庫層面,兩個事務同時更新一條記錄,會發生事務更新覆蓋的狀況,可是在InnoDB中,不會發生這種狀況,事務更新時會加排他鎖,兩個事務順序執行。
  • 另外一個層面是應用層面的丟失更新,在讀取記錄而後更新記錄的場景下,在併發場景下,若是不加控制就會出現覆蓋的狀況。這種狀況能夠經過樂觀鎖和悲觀鎖來解決。

髒讀

髒讀指的是讀取到其餘事務未提交的數據,在讀已提交及以上的隔離級別下就避免該問題。

不可重複讀

不可重複讀指的是同一事務下兩次讀取同一行的數據不一致。InnoDB使用的MVCC機制來實現的。

幻讀

幻讀指的是同一個事務下兩次查詢,返回的記錄數不一致。InnoDB使用的MVCC機制和Next-Key Lock來實現的。

鎖排查

當發生鎖定時,能夠查下information_schema 庫的相關表來查看事務的鎖定狀況來快速定位問題。

查看當前鎖狀況

SELECT
    w.requesting_trx_id,
    t1.trx_state,
    t1.trx_query,
    t1.trx_started,
    t1.trx_wait_started,
    w.requested_lock_id,
    l1.lock_mode,
    l1.lock_type,
    l1.lock_table,
    l1.lock_index,
    l1.lock_data,
    w.blocking_trx_id,
    t2.trx_state,
    w.blocking_lock_id,
    l2.lock_mode,
    l2.lock_type,
    l2.lock_table,
    l2.lock_index,
    l2.lock_data
FROM
    INNODB_LOCK_WAITS AS w
LEFT JOIN INNODB_TRX t1 ON w.requesting_trx_id = t1.trx_id
LEFT JOIN INNODB_TRX t2 ON w.blocking_trx_id = t2.trx_id
LEFT JOIN INNODB_LOCKS l1 ON w.requested_lock_id = l1.lock_id
LEFT JOIN INNODB_LOCKS l2 ON w.blocking_lock_id = l2.lock_id
WHERE
    t1.trx_state = 'LOCK WAIT'
LIMIT 1;
複製代碼

參考資料

dev.mysql.com/doc/refman/…

相關文章
相關標籤/搜索