正常insert數據,MySQL并不會顯式加鎖,而是通過聚簇索引的trx_id索引作為隱式鎖來保護記錄的。比如兩個事務對一個非唯一的索引情況添加,會造成幻讀
但在某些特殊情況下,隱式鎖會轉變為顯式鎖:
- 記錄之間有間隙鎖
- insert的記錄與已有記錄的唯一索引沖突
記錄之間有間隙鎖
每插入一條新記錄,都需要看一下待插入記錄的下一條記錄上是否已經被加了間隙鎖,如果已加間隙鎖,此時會生成一個插入意向鎖,然后鎖的狀態設置為等待狀態(PS:MySQL 加鎖時,是先生成鎖結構,然后設置鎖的狀態,如果鎖狀態是等待狀態,并不是意味著事務成功獲取到了鎖,只有當鎖狀態為正常狀態時,才代表事務成功獲取到了鎖),現象就是 Insert 語句會被阻塞。
# 事務 A
mysql> begin;
Query OK, 0 rows affected (0.01 sec)mysql> select * from t_order where order_no = 1006 for update;# 事務 B 插入一條記錄
mysql> begin;
Query OK, 0 rows affected (0.01 sec)mysql> insert into t_order(order_no, create_date) values(1010,now());
### 阻塞狀態。。。。
可以看到,事務 B 的狀態為等待狀態(LOCK_STATUS: WAITING),因為向事務 A 生成的 next-key 鎖(記錄鎖+間隙鎖)范圍(1005, +∞] 中插入了一條記錄,所以事務 B 的插入操作生成了一個插入意向鎖(LOCK_MODE: X,INSERT_INTENTION),鎖的狀態是等待狀態,意味著事務 B 并沒有成功獲取到插入意向鎖,因此事務 B 發生阻塞。
遇到唯一鍵沖突
插入失敗,會對這條記錄上S型鎖
- 如果主鍵索引重復,插入新記錄的事務會給已存在的主鍵值重復的聚簇索引記錄添加 S 型記錄鎖。
- 如果唯一二級索引重復,插入新記錄的事務都會給已存在的二級索引列值重復的二級索引記錄添加 S 型 next-key 鎖。(但假設有這么一種情況,目前表里有二級索引id=1~5的數據記錄,事務A插入一個id=6,是可以的,不會被阻塞,也沒有上顯式鎖其實,只有隱式鎖。但假設事務B想再插入id=6的數據,A對id=6的隱式鎖會升級為顯式X鎖,B此時相當于想申請id=6的S鎖,會被阻塞)