MySQL中的锁

发布于:2025-06-12 ⋅ 阅读:(36) ⋅ 点赞:(0)

 

博主介绍:✌全网粉丝5W+,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战,博主也曾写过优秀论文,查重率极低,在这方面有丰富的经验✌

博主作品:《Java项目案例》主要基于SpringBoot+MyBatis/MyBatis-plus+MySQL+Vue等前后端分离项目,可以在左边的分类专栏找到更多项目。《Uniapp项目案例》有几个有uniapp教程,企业实战开发。《微服务实战》专栏是本人的实战经验总结,《Spring家族及微服务系列》专注Spring、SpringMVC、SpringBoot、SpringCloud系列、Nacos等源码解读、热门面试题、架构设计等。除此之外还有不少文章等你来细细品味,更多惊喜等着你哦

🍅uniapp微信小程序🍅面试题软考题免费使用,还可以使用微信支付,扫码加群。由于维护成本问题得不到解决,可能将停止线上维护。

🍅文末获取联系🍅精彩专栏推荐订阅👇🏻👇🏻 不然下次找不到哟

Java项目案例《100套》
https://blog.csdn.net/qq_57756904/category_12173599.html
uniapp小程序《100套》

https://blog.csdn.net/qq_57756904/category_12173599.html

有需求代码永远写不完,而方法才是破解之道,抖音有实战视频课程,某马某千等培训都是2万左右,甚至广东有本科院校单单一年就得3万4年就12万学费,而且还没有包括吃饭的钱。所以很划算了。另外博客左侧有源码阅读专栏,对于求职有很大帮助,当然对于工作也是有指导意义等。在大城市求职,你面试来回一趟多多少少都在12块左右,而且一般不会一次性就通过,还得面试几家。而如果你对源码以及微服务等有深度认识,这无疑给你的面试添砖加瓦更上一层楼。

最后再送一句:最好是学会了,而不是学废了!!

2

MySQL 中的锁机制是保证数据一致性、实现事务隔离级别(特别是 SERIALIZABLE)以及协调并发访问的关键。InnoDB 存储引擎实现了复杂的锁系统,主要包含以下几种类型:

一、按锁的模式 (Lock Mode) 分类

  1. 共享锁 (Shared Lock / S Lock / 读锁):

    • 目的: 允许事务读取一行数据。

    • 兼容性: 多个事务可以同时对同一数据加共享锁(兼容)。但不允许任何事务对该数据加排他锁。

    • 获取方式:

      • SELECT ... LOCK IN SHARE MODE

      • 在某些情况下,普通的 SELECT 语句(在 SERIALIZABLE 隔离级别下)也可能隐式加共享锁(InnoDB 在 REPEATABLE READ 和 READ COMMITTED 下通常使用 MVCC 避免加读锁)。

    • 特点: “读读不互斥”。

  2. 排他锁 (Exclusive Lock / X Lock / 写锁):

    • 目的: 允许事务更新或删除一行数据。

    • 兼容性: 一个事务获取某数据的排他锁后,禁止其他任何事务再对该数据加任何类型的锁(共享锁或排他锁)。排他锁之间也互斥。

    • 获取方式:

      • SELECT ... FOR UPDATE

      • UPDATE 语句

      • DELETE 语句

      • INSERT 语句 (通常会对插入的索引记录加隐式排他锁)

    • 特点: “读写互斥”、“写写互斥”。

二、按锁的粒度 (Lock Granularity) 分类

  1. 行级锁 (Row-Level Locks):

    • 粒度: 锁定索引记录(即使表没有显式定义索引,InnoDB 也会创建隐藏的聚簇索引)。

    • 优点: 并发度高,锁冲突概率低。锁定的数据量最小。

    • 缺点: 获取和释放锁的开销相对较大。

    • 实现: InnoDB 主要使用行级锁。行级锁本身也有模式(S锁/X锁)。

    • 注意: InnoDB 的行锁实际上是加在索引上的!如果查询没有使用索引或者无法有效使用索引,行锁可能会升级为表锁(或者锁住整个索引/间隙),导致并发性能急剧下降。

  2. 表级锁 (Table-Level Locks):

    • 粒度: 锁定整张表

    • 优点: 实现简单,加锁/解锁速度快。

    • 缺点: 并发度最低,锁冲突概率高。一个事务锁表会阻塞其他所有需要访问该表的事务。

    • 实现:

      • 显式表锁: LOCK TABLES ... READ/WRITE (通常在 MyISAM 引擎中使用较多,InnoDB 不建议使用,因为它会绕过 InnoDB 的行锁和事务管理)。

      • 隐式表锁:

        • 元数据锁 (MDL): 在执行 DML (SELECT, INSERT, UPDATE, DELETE) 时自动加 MDL 读锁;在执行 DDL (ALTER TABLE, DROP TABLE) 时自动加 MDL 写锁。MDL 读锁之间兼容,但 MDL 写锁会阻塞所有 MDL 锁(读和写)。长事务持有 MDL 读锁会阻塞 DDL 操作。

        • 意向锁: 见下文。

        • 行锁升级: 当 SQL 语句扫描大量行或无法有效使用索引时,InnoDB 可能会将多个行锁升级为一个表锁(虽然不常见,但在特定条件下会发生)。

  3. 意向锁 (Intention Locks):

    • 目的: 协调表级锁和行级锁,避免冲突。它是一种表级锁

    • 工作原理:

      • 事务在申请行锁(S锁或X锁)之前,必须先申请对应的表级意向锁

      • 意向共享锁 (Intention Shared Lock, IS Lock): 表示事务打算在表中的某些行上加共享锁

      • 意向排他锁 (Intention Exclusive Lock, IX Lock): 表示事务打算在表中的某些行上加排他锁

    • 兼容性:

      • 意向锁之间相互兼容(IS 和 IX 可以共存),因为它们只是“意向”,表示稍后会在行上加锁,但还没加。

      • 意向锁不会阻塞全表请求(如 LOCK TABLES ... READ)以外的任何表级锁请求。

      • 表级锁会检查意向锁:

        • 表级共享锁 (LOCK TABLES ... READ):与 IS 锁兼容,与 IX 锁互斥。

        • 表级排他锁 (LOCK TABLES ... WRITE 或 DDL):与 IS 锁和 IX 锁都互斥。

    • 意义: 如果一个事务已经持有了表级 S 锁,另一个事务想申请某行的 X 锁,它需要先申请表的 IX 锁。但表的 S 锁和 IX 锁是互斥的,因此申请 IX 锁会被阻塞,从而避免了行级 X 锁与表级 S 锁的潜在冲突。意向锁让表锁在加锁前就能快速判断表中是否存在冲突的行锁意向,提高了效率。

三、按锁的算法 (Locking Algorithm) 分类 (InnoDB 特有,针对行锁/间隙锁)

InnoDB 在 REPEATABLE READ (默认) 和 READ COMMITTED 隔离级别下,为了在保证一定隔离性的同时解决幻读问题,使用了以下锁算法:

  1. 记录锁 (Record Lock):

    • 锁定: 单个索引记录

    • 模式: S 锁或 X 锁。

    • 示例: SELECT * FROM t WHERE id = 10 FOR UPDATE; 会在 id=10 的索引记录上加 X 型的记录锁。

  2. 间隙锁 (Gap Lock):

    • 锁定: 索引记录之间的间隙,或者第一个索引记录之前、最后一个索引记录之后的“间隙”。它锁定的是一个范围,但不包括记录本身

    • 目的: 防止其他事务在间隙中插入新的记录,从而解决幻读问题。

    • 模式: 只有 S 锁和 S 锁兼容。Gap Lock 本身不区分 S/X,它只阻止插入。多个事务可以在同一个间隙上加 Gap Lock(兼容)。但一个 Gap Lock 会阻止其他事务在该间隙中插入(与插入操作需要的插入意向锁冲突)。

    • 触发条件: 主要在 REPEATABLE READ 隔离级别下使用。

    • 示例: SELECT * FROM t WHERE id BETWEEN 10 AND 20 FOR UPDATE; 除了在 id=10 和 id=20 的记录上加记录锁(X Lock),还会在 (10, 20) 这个开区间上加 Gap Lock,阻止 id 在 11 到 19 之间的记录被插入。

    • 注意: 如果查询条件使用的是唯一索引且是等值查询WHERE unique_col = value),并且查询的值存在,那么 InnoDB 只会加记录锁不会加间隙锁(因为唯一性保证了不会有其他记录插入到这个“点”上)。

  3. 临键锁 (Next-Key Lock):

    • 锁定: 记录锁 + 间隙锁。锁定一个索引记录以及该记录之前的间隙。它锁定的是左开右闭区间 (previous_gap, current_record]

    • 目的: 是 InnoDB 默认的行锁算法。它结合了记录锁和间隙锁的优点,既能锁定单条记录防止修改/删除,又能锁定间隙防止插入,是解决 REPEATABLE READ 级别下幻读的主要手段。

    • 示例: 假设表 t 有记录 id=5, 10, 15。

      • 对 id=10 加 Next-Key Lock:锁定区间 (5, 10]

      • 范围查询 SELECT * FROM t WHERE id > 10 AND id < 15 FOR UPDATE; 可能会在 id=15 上加 Next-Key Lock (锁定 (10, 15]),并可能对 supremum pseudo-record(表示大于最大id的无穷大)加 Gap Lock (锁定 (15, +∞))。

    • 退化: 在特定条件下,Next-Key Lock 会退化为记录锁或间隙锁(如唯一索引等值查询命中记录时退化为记录锁)。

  4. 插入意向锁 (Insert Intention Lock):

    • 类型: 一种特殊的间隙锁 (Gap Lock)

    • 目的: 在插入一行记录之前设置。表示一个事务打算在某个间隙中插入一条记录。

    • 兼容性:

      • 多个事务如果打算插入到同一个间隙的不同位置,它们可以同时持有该间隙的插入意向锁(兼容)。

      • 插入意向锁不会阻止其他事务在同一个间隙上获得插入意向锁。

      • 插入意向锁会与已经存在的 Gap Lock 或 Next-Key Lock 冲突。即,如果一个事务在某个间隙上持有 Gap Lock,另一个事务试图在该间隙插入(需要获取插入意向锁)就会被阻塞。

    • 意义: 提高并发插入性能,同时确保插入操作不会破坏已存在的 Gap Lock。

四、其他重要锁类型

  1. 自增锁 (AUTO-INC Lock):

    • 目的: 保证在具有 AUTO_INCREMENT 列的表上执行 INSERT 操作时,为列生成唯一且连续的值。

    • 特点:

      • 是一种特殊的表级锁

      • 在 INSERT 语句开始时获取,在语句结束时释放(不是事务结束)。

      • 在 MySQL 8.0 之前,所有 INSERT 语句(即使是 INSERT ... SELECT 这种批量插入)都需要获取这个锁,成为性能瓶颈。

      • MySQL 8.0 改进: 引入了轻量级的、基于互斥量(mutex)的自增计数器分配机制(innodb_autoinc_lock_mode = 2),大大提高了高并发插入的性能,牺牲了绝对的连续性(保证唯一性,但不保证连续递增)。模式 1 (interleaved) 和 0 (traditional) 仍然使用或部分使用表级 AUTO-INC 锁。

五、死锁 (Deadlock)

  • 定义: 两个或多个事务在执行过程中,因争夺锁资源而造成的一种相互等待的现象,若无外力干预,它们都将无法进行下去。

  • InnoDB 处理:

    • 检测: InnoDB 使用等待图 (wait-for graph) 机制来检测死锁。如果发现有环,就说明发生了死锁。

    • 解决: 当检测到死锁时,InnoDB 会回滚其中一个代价最小的事务(通常是修改行数最少的事务),让其他事务得以继续执行。被回滚的事务会收到一个 1213 错误 (Deadlock found when trying to get lock; try restarting transaction)。

  • 避免策略:

    • 尽量以相同的顺序访问表和行。

    • 在事务中,尽早提交尽早释放不需要的锁

    • 使用合理的索引,减少锁的范围和冲突。

    • 如果业务允许,可以尝试降低隔离级别(如 READ COMMITTED 可以减少间隙锁的使用)。

    • 设置合理的锁等待超时 (innodb_lock_wait_timeout),避免长时间阻塞(但超时本身不是解决死锁,而是解决锁等待过长)。

六、锁等待与监控

  • 锁等待: 当一个事务尝试获取一个被其他事务持有的、不兼容的锁时,它必须等待直到那个锁被释放。

  • 监控工具:

    • SHOW ENGINE INNODB STATUS;:查看 TRANSACTIONS 和 LATEST DETECTED DEADLOCK 部分。

    • INFORMATION_SCHEMA.INNODB_TRX:查看当前运行的事务信息(包括等待的锁)。

    • INFORMATION_SCHEMA.INNODB_LOCKS:查看当前存在的锁信息。

    • INFORMATION_SCHEMA.INNODB_LOCK_WAITS:查看锁等待关系。

    • Performance Schema (performance_schema):提供更细粒度的锁监控(如 events_waits_currentmutex_instancesrwlock_instances 等表,需要启用相关 instruments 和 consumers)。

总结:

MySQL InnoDB 的锁机制是一个多层次、多类型的复杂系统:

  1. 模式: 共享锁 (S) 和 排他锁 (X) 定义了基本的互斥规则。

  2. 粒度: 行级锁提供高并发,表级锁(包括意向锁 IS/IX 和 MDL)用于协调和管理。

  3. 算法: 记录锁、间隙锁、临键锁(Next-Key Lock 是默认)、插入意向锁共同作用,在 REPEATABLE READ 级别下解决幻读问题并保证一致性。

  4. 特殊锁: 自增锁 (AUTO-INC) 用于保证自增列的唯一性。

  5. 并发问题: 死锁由引擎自动检测并回滚事务解决。

  6. 监控: 利用系统表和信息诊断锁相关问题。

理解这些锁的类型、作用、兼容性以及在不同隔离级别下的行为,对于设计和优化高并发数据库应用、诊断性能问题和死锁至关重要。合理设计索引、事务和SQL语句能有效减少锁冲突,提升系统并发能力。

3