前言
如果希望直接獲取結論,請看末尾【一些結論】模塊
- mysql 版本 5.7
- 引擎 innodb
- 隔離級別 RR (RepeatableRead)
- 需要了解 mysql 的 next-key 鎖(間隙鎖 + 索引記錄鎖)
- 本文加鎖使用讀取鎖定(也可使用 delete/update … where … )
- 設置 innodb_status_output_locks ,然後可以使用 show engine innodb status 查看詳細的鎖信息
- 需要了解聚簇索引和非聚簇索引
- 剛開始學習這塊知識,有錯誤,歡迎指出
表結構(id 上建立了主鍵索引,price 列建立了普通索引,order_num 列建立了唯一索引)
CREATE TABLE `order_info` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`price` int(11) DEFAULT NULL,
`order_num` int(11) DEFAULT NULL,
`user_id` int(10) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_order_num` (`order_num`),
KEY `idx_price` (`price`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
原始數據
+----+-------+-----------+---------+
| id | price | order_num | user_id |
+----+-------+-----------+---------+
| 1 | 1000 | 1010 | 1 |
| 2 | 2000 | 1020 | 5 |
| 3 | 3000 | 1030 | 2 |
| 4 | 4000 | 1040 | 1 |
| 5 | 6000 | 1050 | 10 |
+----+-------+-----------+---------+
一些 sql 解析
由於數據量比較少,所以,走非主鍵索引的時候,使用 force index
驗證間隙鎖的時候,需要保持第一個事務不釋放
關於如何 show engine innodb status;
命令中查看鎖的詳細信息,我在文後附了幾個鏈接,可以先了解下。推薦這個鏈接MySQL鎖系列(二)之 鎖解讀
1.1、走主鍵索引:select * from order_info where id = 1 for update
查看鎖信息:
- 主鍵索引上申請了索引記錄鎖
- 記錄鎖鎖的 id 字段爲 1
1.2、走主鍵索引:select * from order_info where id > 2 for update
注意: 這個測試之前,需要將 id 字段的 unsigned 撤銷,不然無法設置 id 爲 -1.測試完之後,需要再恢復爲 unsigned
查看鎖信息:
1.主鍵索引申請了 next-key 鎖,鎖的間隙(2,+∞)
1.3、無任何索引:select * from order_info where user_id = 1 for update
鎖信息:
1.主鍵索引申請了 next-key 鎖,鎖的間隙爲聚簇索引的所有間隙,即(-∞,1)(1,2)(2,3)(3,4)(4,5)(5,+∞)
1.4、無任何索引:select * from order_info where user_id >4 for update
鎖信息:
- 主鍵索引申請了 next-key 鎖,鎖的間隙爲聚簇索引的所有間隙,即(-∞,1)(1,2)(2,3)(3,4)(4,5)(5,+∞)
1.5、普通索引:select * from order_info force index(idx_price) where price = 3000 for update
鎖信息:
- idx_price 索引申請了 next-key 鎖( 範圍爲 (2000,3000) (3000,4000) )
- 主鍵索引申請了索引記錄鎖
驗證間隙鎖範圍:
price 爲 2001和3999 的記錄需要等待間隙鎖才能插入,而 price 爲 1999 和 4000的記錄不需要獲取間隙鎖,可以直接插入
1.6、普通索引:select * from order_info force index(idx_price) where price > 3000 for update
鎖信息:
- idx_price 索引申請了 next-key 鎖(範圍:(3000,+∞))
- 主鍵索引申請了索引記錄鎖
新開一個事務,驗證間隙鎖範圍:
插入price 爲3001,4001,9999 的記錄需要申請間隙鎖;插入插入price 爲 2999,100的記錄不需要申請間隙鎖
1.7、唯一索引:select * from order_info force index(idx_order_num) where order_num = 1030 for update
鎖信息:
- 唯一索引 idx_order_num 上申請索引記錄鎖
- 主鍵索引申請了索引記錄鎖
1.8、唯一索引:select * from order_info force index(idx_order_num) where order_num > 1030 for update
鎖信息:
- 索引 idx_order_num 申請了間隙鎖和索引記錄鎖
- 主鍵索引申請了索引記錄鎖
一些結論
- innodb 通過 next-key 鎖可以解決幻讀問題
- 針對主鍵索引
- 精確查詢只申請索引記錄鎖
- 範圍查詢會申請 next-key 鎖
- 針對普通索引
- 精確查詢會申請 next-key 鎖
- 範圍查詢會申請 next-key 鎖
- 針對唯一索引
- 精確查詢會申請 索引記錄鎖
- 範圍查詢會申請 next-key 鎖