mysql InnoDB引擎各种隔离级别的加锁机制

[复制链接]
发表于 2026-2-1 23:35:29 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
概要

  我们都知道,mysql的InnoDB引擎在各种隔离级别下的加锁机制都是有差异的,但是对于各种隔离级别下怎样加锁各人大概不太相识,本日我就通过一篇文章去领导各人去分析一下各个隔离级别的加锁过程,假如有误,接待各人在批评区指正!
本日实行的表结构界说如下,接待各人本身去实行验证。我的mysql版本是5.7。此中id加上了唯一索引为了验证行锁的加锁过程。age加上非唯一索引,给各人演示一下gap的加锁过程。:
  1. -- 学生表,id,姓名,年纪三个字段。
  2. CREATE TABLE `t_stu` (
  3.   `id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品ID',
  4.   `name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '会员卡名称',
  5.   `age` int NOT NULL,
  6.   PRIMARY KEY (`id`) USING BTREE,
  7.   KEY `name` (`name`),
  8.   KEY `age` (`age`) USING BTREE
  9. ) ENGINE=InnoDB AUTO_INCREMENT=78 DEFAULT CHARSET=utf8mb3 COMMENT='会员商品表';
  10. INSERT INTO `t_stu` (`id`, `name`, `age`) VALUES (1, '小张', 1);
  11. INSERT INTO `t_stu` (`id`, `name`, `age`) VALUES (3, '小王', 3);
  12. INSERT INTO `t_stu` (`id`, `name`, `age`) VALUES (4, '小美', 4);
  13. INSERT INTO `t_stu` (`id`, `name`, `age`) VALUES (44, '小美', 44);
复制代码
前置知识相识

读锁和写锁的互斥情况,只有读锁和读锁可以并行实行,其他的情况都是互斥的。
读锁写锁读锁√x写锁xx

  • mysql的表级锁有哪些,此中我们须要重点关注的就是元数据锁:
    1. 表锁:通常意义上我们是不会给表自动加锁。通过 LOCK TABLES t_stu READ,LOCK TABLES t_stu WRITE去加锁。通过unlock tables去解锁。在实际的业务过程中题主暂时未碰到过使用场景,以是这个锁不在我们的讨论范围。
    2. 元数据锁(MDL):这个锁我们须要关注一下,我们在表结构变更时会加一个MDL写锁,就是当我们操纵表做DDL语句时,比方:给表加字段,给表加索引。当我们给做crud语句时会给表加MDL读锁。以是在高并发的场景下,不可以去对表做DDL操纵,会锁表。(可以通过 online DDL举行ddl和dml的并行操纵) 由于DDL语句不会被事物控制,以是对于小批量的数据题主没办法演示加锁情况,各人可以自行多加点数据去做演示。
    3. 意向锁:题主没关注这个。
    4. AUTO-INC 锁:主键索引自增时加的一个锁。
  • mysql的行锁有哪些:
    1. 行级锁: 加在索引上的锁,假如表上没有任何索引,InnoDB会使用一个内部的行ID来锁定行。在RR隔离级别下,我们在没有索引的表上去修改数据,着实是锁定了全部的行+加上行的间隙,并不是锁表。
    2. 间隙锁 GAP LOCK: 间隙锁是RR隔离级别下加在非唯一索引的两个数据之间的锁,留意好坏唯一索引,理论上我们对主键索引去修改数据时是不会加间隙锁的。别的一种情况就是where范围查询时,锁定主键索引的区间。留意间隙锁只有在RR隔离级别下才存在。
    3. next-key lock: 行级锁+间隙锁构成的锁。
各种隔离锁的验证



  • 读未提交 (READ UNCOMMITTED): 按照次序实行,我们会发现事物2在实行到第四步时被壅闭了,分析在读未提交的情况下是有行级锁存在的。并不是网上说的行级锁只存在于读已提交和可重复读的隔离级别。
  1. -- 事物1 ----------------------------------------------------------
  2. -- 1
  3. SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  4. BEGIN;
  5. -- 3
  6. UPDATE `t_stu` SET `name` = 'aa' WHERE `id` = 1;
  7. COMMIT;
  8. -- 事物2 ----------------------------------------------------
  9. -- 2
  10. SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  11. BEGIN;
  12. -- 4
  13. UPDATE `t_stu` SET `name` = 'aaa' WHERE `id` = 1;
  14. COMMIT;
复制代码
通过下令检察加锁状态,可以看到数据都被加了X锁,就是排它锁。




  • 读已提交 (READ COMMITTED):
  1. -- 事物1 ----------------------------------------------------------
  2. -- 1
  3. SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
  4. BEGIN;
  5. -- 3
  6. -- UPDATE `t_stu` SET `name` = 'aa' WHERE `id` = 1;
  7. SELECT * FROM `t_stu` WHERE age = 4 LOCK IN SHARE MODE;
  8. -- 5
  9. SELECT * FROM `t_stu` WHERE age = 4 LOCK IN SHARE MODE;
  10. -- 7
  11. COMMIT;
  12. -- 事物2 ----------------------------------------------------
  13. -- 2
  14. SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
  15. BEGIN;
  16. -- 4
  17. INSERT INTO `jl`.`t_stu` (`id`, `name`, `age`) VALUES (10, '小小美', 4);
  18. -- 6
  19. COMMIT;
复制代码
留意这块的查询语句我加上了S共享锁,由于mysql默认查询时SELECT语句是默认不加锁的,以是为了方便演示我加上了读共享锁。上述语句我们在实行时,是能乐成的插入数据的,通过锁分析也可得知在RC隔离级别下,对于平常索引的数据我们只加了行级锁,但是没有加gap lock的。当我们实行到步调5,第六步未实行的情况下,我们看一下加锁状态,写哀求加了X排它锁,读数据我加了共享锁。以是从分析就可以得知在RC隔离级别下,就算是平常的非唯一索引age,也不会加gap锁。



  • 可重复读 (REPEATABLE READ):
  1. -- 事物1 ----------------------------------------------------------
  2. -- 1
  3. SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
  4. BEGIN;
  5. -- 3
  6. -- UPDATE `t_stu` SET `name` = 'aa' WHERE `id` = 1;
  7. SELECT * FROM `t_stu` WHERE age = 4 LOCK IN SHARE MODE;
  8. -- 5
  9. SELECT * FROM `t_stu` WHERE age = 4 LOCK IN SHARE MODE;
  10. -- 7
  11. COMMIT;
  12. -- 事物2 ----------------------------------------------------
  13. -- 2
  14. SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
  15. BEGIN;
  16. -- 4
  17. INSERT INTO `jl`.`t_stu` (`id`, `name`, `age`) VALUES (10, '小小美', 4);
  18. -- 6
  19. COMMIT;
复制代码
当我们在实行步调4时着实就会被壅闭了,数据是没办法插入乐成的,由于此时在age的的非唯一索引上加了age=4的数据的行级别锁,在非主键键索引上在age(3,4)(4,44)之间加了gap lock。导致了数据插入时就会锁辩说,没办法举行数据插入。

我们在演示一个其他的例子,我们把上面事物2的插入的id为10的age数据改成64。先看一数据库的数据:

  1. -- 事物2 ----------------------------------------------------
  2. -- 2
  3. SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
  4. BEGIN;
  5. -- 4
  6. INSERT INTO `jl`.`t_stu` (`id`, `name`, `age`) VALUES (10, '小小美', 64);
  7. -- 4
  8. -- 6
  9. COMMIT;
复制代码
终极发现,实行到步调4时,数据是可以插入乐成的,那就是由于,我们age为64的数据不在gap锁锁定age的范围(3,4),(4,44)范围内。
小结

  通过上面的各种的案例演示,给各人展示了各种隔离级别下的数据的加锁状态。大概有些知识点是冲破可各人对于加锁状态的认知的,各人可以通过本地数据库去模拟一下各种隔离级别的数据加锁状态,会加深各人对于这些知识的认知:行锁什么时间加,gap锁什么时间加,gap锁的锁定范围这些
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表