MySQL行级锁——技术深度+1

引言

本文是对MySQL行级锁的学习,MySQL一直停留在会用的阶段,需要弄清楚锁和事务的原理并DEBUG查看。

PS:本文涉及到的表结构均可从https://github.com/WeiXiao-Hyy/blog中获取,欢迎Star!

MySQL行级锁

行级锁(Row-Level-Locking)是InnoDB引擎特有的特性

  • 共享锁(S锁):一个事务去加共享锁后,同时也允许其他事务读,但是排斥其他事务获取排他锁(X锁)
  • 排他锁(X锁):一个事务加排他锁后,其他事务不允许加X锁,也不允许加S锁。

不同的语句会加上不同的锁

  • insert,update,delete,select... for update (X锁)
  • select (不会加锁,快照读)
  • select... for share (S锁)

MySQL中行级锁

  • 记录锁(Record Lock)
  • 间隔锁(Gap Lock)
  • 临键锁(Next-Keys Lock)

行级锁并非是将锁加到记录上,而是加到了索引上

InnoDB引擎在RR事务隔离级别下使用临键锁搜索和索引扫描,从而防止幻读,该给索引记录上什么样的锁,要根据具体情况而定,不过MySQL都是首先考虑临键锁,根据不同的情况退化为记录锁或者间隙锁。

InnoDB引擎在RR事务隔离级别下使用临键锁搜索和索引扫描,从而防止幻读,索引当分析一个SQL加什么行级锁的时候要使用考虑以下几件事:

  1. 临键锁区间情况
  2. 通过条件确定索引数据范围,判断命中了哪些临键锁区间
  3. 判断是否可能退化为记录锁或者间隙锁
text 复制代码
mysql> CREATE TABLE `t1` (
    ->   `id` int NOT NULL AUTO_INCREMENT,
    ->   `name` varchar(10) NOT NULL,
    ->   `id_nbr` varchar(19) DEFAULT NULL,
    ->   `age` int NOT NULL,
    ->   PRIMARY KEY (`id`),
    ->   UNIQUE KEY `idx_uk_id_nbr` (`id_nbr`),
    ->   KEY `idx_name` (`name`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Query OK, 0 rows affected (0.02 sec)

mysql> insert into t1 values(3, '刘备', '110101193007282815', 93), (5, '孙权', '110101194007281016', 93), (6, '曹操', '110101191807288714', 95), (9, '王朗', '110101190007287516', 123);
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> select * from t1;
+----+--------+--------------------+-----+
| id | name   | id_nbr             | age |
+----+--------+--------------------+-----+
|  3 | 刘备   | 110101193007282815 |  93 |
|  5 | 孙权   | 110101194007281016 |  93 |
|  6 | 曹操   | 110101191807288714 |  95 |
|  9 | 王朗   | 110101190007287516 | 123 |
+----+--------+--------------------+-----+
4 rows in set (0.00 sec)

聚簇索引

记录存在

  • select * from t1 where id <= 3 for update; -> LOCK_MODE: X(临键锁), (-∞,3]
  • select * from t1 where id < 3 for update; -> LOCK_MODE: X,GAP(间隔锁), (-∞,3)
  • select * from t1 where id = 3 for update; -> LOCK_MODE: X,REC_NOT_GAP(记录锁), 3,3
  • select * from t1 where id <= 3 for share; -> LOCK_MODE: S,REC_NOT_GAP(共享锁)

记录不存在

  • select * from t1 where id = 7 for update;

由于id=7会落在(6,9]这个区间,但是查询条件没有9,因此退化为间隔锁,锁的范围为(6,9)

  • select * from t1 where id = 10 for update;

由于上边界索引key值不存在的时候,锁为临键锁,锁的范围为(9, +∞) LOCK_DATA=supremum pseudo-record

  • select * from t1 where id <= 4 for update;

命中了两个临键区(-∞,3]和(3,5], 第一个临键区不会退化,所以会加上一个上界=3的临键锁,对于第二个临键区,查询条件中不包含5,所有退化为间隔锁(3,5)

  • select * from t1 where id > 6 and id ≤ 9 for update;

刚刚好命中(6,9]临键区,但实际上会添加上一个最大索引记录值LOCK_DATA=supremum pseudo-record 原因当扫描到9(最大索引值)之后,还会继续向后扫描。即为(6,9]+(9,+∞)

二级唯一索引

二级唯一索引都是使用临键区和索引数据。

非聚簇唯一索引范围查询:InnoDB存储引擎使用临键锁搜索数据,会搜索到下一个不满足条件的索引KEY,如果进入到下一个临键区,则会将下一个临键区加上临键锁(任何时候都不会退化,这跟主键索引是不同的)

  • select * from t1 where id_nbr <= '110101190007287516' for update;

    id_nbr <= '110101190007287516'会进入到下一个临主键区,并且不会退化;

对于为什么会锁住该记录的主键值

如果不对主键索引加锁,并发操作就能通过id_nbr之外的条件修改id_nbr = '110101190007287516'的记录。比如:delete from t1 where name = '王朗';

  • select * from t1 where id_nbr < '110101190007287516' for update;

id_nbr < '110101190007287516'不会进入到下一个临主键区,并且不会退化;

  • select * from t1 where id_nbr = '110101190007287516' for update;

如果条件只是等于一个已经存在的记录110101190007287516,则会退化为记录锁。

  • select * from t1 where id_nbr != '110101190007287515' for update;

如果条件只是等于一个不存在的记录110101190007287516,则会退化为间隔锁。

二级普通索引

二级普通索引唯一不同的在于等值查询

  • select * from t1 where name = 'caocao' for update;

正常来说会命中临键区(-∞, caocao],然后退化为记录锁。但是实际上普通索引是允许重复,记录锁会导致出现幻读,结果为

  1. 临键区不退化
  2. 第二个退化为间隔锁

补充

查看MySQL的隔离级别

sql 复制代码
select @@transaction_isolation;

查看MySQL8.0的锁

sql 复制代码
SELECT * FROM performance_schema.data_locks;

SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE, LOCK_STATUS, LOCK_DATA FROM performance_schema.data_locks;

参考资料

相关推荐
程序猿秃头之路15 小时前
DDD 系列:实体和值对象详解
数据库·oracle·ddd·领域驱动设计
Database_Cool_15 小时前
阿里云 RDS MySQL vs 腾讯云 CDB 国内云关系型数据库深度对比
数据库·mysql·阿里云
xiaohaiAIgeo16 小时前
【2026年】基于三维建模的实验室智慧管理平台:暖通能源照明的数字孪生方案
大数据·数据库·人工智能·科普知识
深盾科技_Virbox16 小时前
软件授权工具静默安装实践
java·运维·数据库·安全·软件需求
延凡科技17 小时前
延凡科技综合监控预警处置平台—— 一体化视频AI安防闭环系统设计与功能实现[特殊字符]️
数据库·人工智能·科技·安全·能源
老马聊技术17 小时前
Rocky Linux 9.0 安装 MySQL8.x详细教程
linux·数据库
醇氧17 小时前
主流 Agent 开发框架全解析(2026 最新)
大数据·数据库·人工智能·开源
吴声子夜歌17 小时前
Redis 6.x——整合SpringBoot
数据库·spring boot·redis
ATA888817 小时前
AI辅助生成SQL实战从连接配置到执行计划优化的完整技术流程
数据库·人工智能·智能问数
咏方舟【长江支流】17 小时前
【开源】跨语言·跨平台·跨数据库(4) ——用宝框架 ORM 数据源适配器
数据库·开源·ai编程·orm·咏方舟-长江支流·用宝框架·用宝框架orm