Mysql的InnoDB存儲引擎鎖機制

1. 事務

1.1 事務概述

  事務用來保證數據庫的完整性——要麼都修改,要麼都不修改。事務必須知足ACID四個特性。mysql

  • 原子性(atomicity),指整個數據庫事務是不可分割的工做單位。只有使事務中全部的數據庫操做執行都成功,纔算整個事務成功。若是事務中任何一個SQL語句執行失敗,那麼已經執行成功的SQL語句也必須撤銷,數據庫狀態應該退回到執行事務前的狀態。
  • 一致性(consistency),指事務將數據庫從一種狀態轉變爲下一種一致的狀態,在事務開始以前和事務結束之後,數據庫的完整性約束沒有被破壞。
  • 隔離性(isolation),一個事務的影響在該事務提交前對其餘事務都不可見——這經過鎖來實現。
  • 持久性(durability),事務一旦提交,其結果就是永久性的。即便發生宕機等故障,數據庫也能將數據恢復。

1.2 事務隔離級別

  ISO和ANIS SQL標準制定了四種事務隔離級別的標準:算法

  • READ UNCOMMITEED,讀未提交,會出現髒讀問題。
  • READ COMMITTED,讀已提交,會出現幻讀問題。
  • REPEATABLE READ,可重複讀(InnoDB存儲引擎的默認隔離級別)
  • SERIALIZABLE,會給每個讀操做加一個共享鎖,不支持一致性的非鎖定讀,隔離性最高。

2. 鎖的粒度和類型

  InnoDB既支持行級鎖,也支持表級鎖,默認狀況下是採用行級鎖。
InnoDB存儲引擎實現了兩種標準的行級鎖:sql

  • 共享鎖(S Lock),容許事務讀一行數據,不容許其餘事務獲取排他鎖。
  • 排他鎖(X Lock),容許事務刪除或者更新一行數據,不容許其餘事務得到共享鎖或排他鎖。

  InnoDB存儲引擎支持多粒度鎖定,容許行級鎖和表級鎖同時存在。爲了支持在不一樣粒度上進行加鎖操做,InnoDB存儲引擎提供了意向鎖。意向鎖是表級別的鎖,其設計目的主要是爲了在一個事務中揭示下一行將被請求的鎖的類型。InnoDB存儲引擎支持兩種意向鎖:數據庫

  • 意向共享鎖(IS Lock),事務想要得到一個表中某幾行的共享鎖。
  • 意向排他鎖(IX Lock),事務想要得到一個表中某幾行的排他鎖。

  意向鎖不會阻塞除全表掃描之外的任何請求。併發

3. 一致性的非鎖定讀

  一致性的非鎖定讀(consistent nonlocking read)是指InnoDB存儲引擎經過多版本控制(multi versioning)的方式來讀取當前執行時間數據庫中行的數據。若是讀取的行正在執行DELETE、UPDATE操做,這是讀取操做不會所以而等待行上鎖的釋放,相反,InnoDB存儲引擎會去讀取行的一個快照數據。以下圖所示:測試

      image

  上圖能夠看出,讀取的數據是一份快照數據,快照數據是指執行當前更改操做前的數據,該實現是經過undo段來實現的,因此快照數據自己是沒有額外的開銷的。由於快照數據是一份歷史數據,是隻讀的,因此不須要上鎖。
  快照可能有多個版本,也就是說可能有多份不一樣的快照數據,這種技術稱爲行多版本技術,由此帶來的併發控制稱爲多版本併發控制(Multi Version Concurrency Control, MVCC)
  一致性非鎖定讀是InnoDB存儲引擎默認的讀取方式,可是不一樣的事務隔離級別下,讀取的方式不一樣,並非每一個事務隔離級別下讀取的都是一致性讀,即便都是一致性讀,不一樣的事務隔離級別讀取的快照數據也不一樣。例如Read Commited和Repeatable Read下,使用的都是一致性的非鎖定讀,但它們讀取的是不一樣的快照數據。在Read Commited級別下,讀取的老是被鎖定行的最新一份快照數據,而在Repeatable Read級別下,讀取的是事務開始時的第一份快照數據。atom

step 1. 初始化測試表
mysql> create table t (id int,
    -> primary key (id))
    -> engine innodb;
Query OK, 0 rows affected (0.62 sec)
mysql> insert into t values(1);
mysql> select * from t;
+----+
| id |
+----+
|  1 |
+----+
1 row in set (0.00 sec)

step 2. 開啓一個會話A,並在會話A中開啓一個事務,查看下測試表的數據,但不提交事務
# Session A
mysql> begin;
Query OK, 0 rows affected (0.03 sec)

mysql> select * from t;
+----+
| id |
+----+
|  1 |
+----+
1 row in set (0.00 sec)

step 3. 開啓另外一個會話B,模擬併發的狀況,在會話B中開啓事務,修改測試表中id爲1的數據,但不提交
# Session B
mysql> begin ;
Query OK, 0 rows affected (0.00 sec)
mysql> update t set id = 3 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

step 4. 在會話A中查看測試表數據,發現仍是修改以前的數據
# Session A
mysql> select * from t;
+----+
| id |
+----+
|  1 |
+----+
1 row in set (0.00 sec)

setp 5. 在會話B中提交事務
# Session B
mysql> commit;
Query OK, 0 rows affected (0.04 sec)

step 5. 再在會話A中查看測試數據,在Read Commited和Repeatable Read級別下獲得的結果就不同了。對於Read Committed級別,它老是讀取最新版本,因此它獲得的結果是一個id爲3的記錄(幻讀)。而Repeatable Read級別下,它老是讀取事務開始時的行數據,因此它獲得的結果仍然是一個id爲1的記錄。

4. Select ... For Update 和 Select ... Lock In Share Mode

  默認狀況下,InnoDB存儲引擎的Select操做使用的是一致性非鎖定讀,可是有些狀況下,須要用戶主動對讀取操做進行加鎖。InnoDB存儲引擎對Select語句加鎖有兩種操做:spa

  • Select ... For Updata,對讀取的行記錄加一個X鎖。其餘事務想在這些行上加任何鎖都會被阻塞。
  • Select ... Lock In Share Mode,對讀取的行加一個S鎖。其餘事務能夠向被鎖定的行加S鎖,可是若是加X鎖會被阻塞。

  對於一致性非鎖定讀,即便讀取的行已被使用Select ... For Update,也是能夠讀取的。Select ... For Update 和 Select ... Lock In Share Mode 必須在事務中使用,當事務提交了,鎖也就釋放了。設計

5. 鎖的算法

  InnoDB存儲引擎有3種行鎖算法:版本控制

  • Record Lock,單個記錄上的鎖。Record Lock鎖住的永遠是索引而不是記錄,若是在建表的時候沒有設置索引,InnoDB存儲引擎會使用隱式的主鍵來進行鎖定。
  • Gap Lock,間隙鎖,鎖定一個範圍,但不包含記錄自己。Gap Lock主要是解決可重複度模式下的幻讀問題。
  • Next-Key Lock,Gap Lock + Record Lock,鎖定一個範圍,而且包括記錄自己。

6. 鎖問題

6.1 丟失更新

  多個事務同時修改同一行記錄,可能出現丟失更新的問題。

  1. 事務A查詢了一行數據。
  2. 事務B也查詢了這行數據。
  3. 事務A根據它的查詢結果修改數據,並提交事務。
  4. 事務B根據它的查詢結果修改數據,並提交事務。

  能夠看出,事務1的更新操做丟失了。避免丟失更新的作法,就是事務在查詢數據的時候加上排他鎖。

6.2 髒讀

  髒讀是指事務A讀到了事務B中尚未提交的更新(違反了數據庫的隔離性)。髒讀只在隔離級別爲Read UnCommitted的狀況下才會發生。避免髒讀的辦法,就是把事務的隔離級別至少設置成Read Committed。

6.3 幻讀(不可重複讀)

  幻讀是指在一個事務中屢次讀同一數據,拿到的結果不一樣(違反了數據庫的一致性)。例如,事務A讀了一行數據,事務B修改了這行數據並提交了,事務A再讀這行數據,拿到的結果與事務A前一次讀取的結果不一樣。  幻讀通常是能夠接收的,由於它讀到的確實是其餘事務已經提交的數據。  InnoDB存儲引擎中,經過使用Next-Key Lock算法來避免幻讀問題。在Next-Key Lock算法下,對於索引的掃描,不只僅鎖住的是掃描到的索引,並且還鎖住這些索引覆蓋的範圍(gap),所以在這個範圍內的修改都是不容許的。

相關文章
相關標籤/搜索