從一些例子看 mysql innodb 在 RR 隔離級別的 next-key 鎖的行爲

前言

如果希望直接獲取結論,請看末尾【一些結論】模塊

  • 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

查看鎖信息:
在這裏插入圖片描述

  1. 主鍵索引上申請了索引記錄鎖
  2. 記錄鎖鎖的 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

鎖信息:
在這裏插入圖片描述

  1. 主鍵索引申請了 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

鎖信息:
在這裏插入圖片描述

  1. idx_price 索引申請了 next-key 鎖( 範圍爲 (2000,3000) (3000,4000) )
  2. 主鍵索引申請了索引記錄鎖

驗證間隙鎖範圍:
在這裏插入圖片描述
price 爲 2001和3999 的記錄需要等待間隙鎖才能插入,而 price 爲 1999 和 4000的記錄不需要獲取間隙鎖,可以直接插入

1.6、普通索引:select * from order_info force index(idx_price) where price > 3000 for update

鎖信息:
在這裏插入圖片描述

  1. idx_price 索引申請了 next-key 鎖(範圍:(3000,+∞))
  2. 主鍵索引申請了索引記錄鎖

新開一個事務,驗證間隙鎖範圍:
在這裏插入圖片描述
插入price 爲3001,4001,9999 的記錄需要申請間隙鎖;插入插入price 爲 2999,100的記錄不需要申請間隙鎖

1.7、唯一索引:select * from order_info force index(idx_order_num) where order_num = 1030 for update

鎖信息:
在這裏插入圖片描述

  1. 唯一索引 idx_order_num 上申請索引記錄鎖
  2. 主鍵索引申請了索引記錄鎖

1.8、唯一索引:select * from order_info force index(idx_order_num) where order_num > 1030 for update

鎖信息:
在這裏插入圖片描述

  1. 索引 idx_order_num 申請了間隙鎖和索引記錄鎖
  2. 主鍵索引申請了索引記錄鎖

一些結論

  1. innodb 通過 next-key 鎖可以解決幻讀問題
  2. 針對主鍵索引
    1. 精確查詢只申請索引記錄鎖
    2. 範圍查詢會申請 next-key 鎖
  3. 針對普通索引
    1. 精確查詢會申請 next-key 鎖
    2. 範圍查詢會申請 next-key 鎖
  4. 針對唯一索引
    1. 精確查詢會申請 索引記錄鎖
    2. 範圍查詢會申請 next-key 鎖

Reference

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章