Administrator
发布于 2026-01-19 / 2 阅读
0

面鸭突击30天之8d

MySQL 中有哪些锁类型?

(一)按锁粒度划分

锁的粒度从大到小分为表锁行锁,其中行锁又细分为记录锁、间隙锁、临键锁(Next-Key Lock),此外还有特殊的意向锁(辅助表锁与行锁的判断)。

  1. 表锁

    • 锁定整个数据表,粒度最大,并发性能最差,分为表共享读锁(S 锁)和表排他写锁(X 锁)。

    • 手动上锁 SQL:

    • 表共享读锁:LOCK TABLE xx READ;(其他事务可读,不可写)

    • 表排他写锁:LOCK TABLE xx WRITE;(其他事务既不可读,也不可写)

    • 适用场景:无索引操作、全表扫描更新等,InnoDB 中极少主动使用,MyISAM 引擎的默认锁类型。

  2. 行锁

    • 仅锁定数据表中指定的行记录,粒度最小,并发性能最优,仅 InnoDB 引擎支持(依赖索引实现,无索引时行锁退化为表锁)。

    • 细分类型:

      行锁类型

      核心作用

      生效隔离级别

      补充说明

      记录锁(Record Lock)

      锁定索引上的具体一条记录,防止该记录被修改/删除

      读已提交(RC)、可重复读(RR)

      仅锁定已存在的索引记录,不限制间隙插入;无索引时会全表加记录锁,等效表锁

      间隙锁(Gap Lock)

      锁定索引记录之间的间隙(包括不存在的虚拟记录),防止其他事务插入数据,避免幻读

      可重复读(RR)

      不阻塞同间隙的查询操作,仅阻塞插入操作;仅对非唯一索引生效,唯一索引(主键/唯一约束)无间隙锁

      临键锁(Next-Key Lock)

      记录锁+间隙锁的组合,锁定左开右闭的索引区间

      可重复读(RR)

      InnoDB中RR隔离级别下行锁的默认形态,既锁定当前记录,也锁定记录前后的间隙,彻底防止幻读

      插入意向锁

      特殊的间隙锁,仅在插入数据时触发

      可重复读(RR)

      多个事务向同一间隙插入数据时,插入意向锁之间互不阻塞,仅等待已存在的间隙锁释放

  3. 意向锁

    • 辅助表锁的一种 “标记性锁”,用于快速判断数据表内是否存在行锁,避免全表遍历检查行锁,提升锁判断效率,仅 InnoDB 引擎支持。

    • 细分类型:

    • 意向共享锁(IS 锁):对表内某条记录上共享锁(S 锁)前,先为整个表上 IS 锁。

    • 意向排他锁(IX 锁):对表内某条记录上排他锁(X 锁)前,先为整个表上 IX 锁。

    • 核心规则:意向锁与表锁互斥,与其他意向锁兼容;仅用于锁判断,不直接阻塞业务操作。

(二)按锁模式划分

分为共享锁(S 锁,读锁)*和*排他锁(X 锁,写锁),是 MySQL 锁的基础模式。

  1. 共享锁(S 锁)

    • 核心权限:允许持有锁的事务读取数据,不允许修改数据;允许多个事务同时持有同一资源的 S 锁(共享兼容)。

    • 上锁 SQL:SELECT ... FROM ... LOCK IN SHARE MODE;(行级共享锁);LOCK TABLE xx READ;(表级共享锁)。

    • 兼容性:与 S 锁兼容,与 X 锁互斥。

  2. 排他锁(X 锁)

    • 核心权限:允许持有锁的事务读写数据,不允许其他事务持有该资源的任何锁(排他不兼容)。

    • 上锁 SQL:SELECT ... FROM ... FOR UPDATE;(行级排他锁);LOCK TABLE xx WRITE;(表级排他锁);DML 操作(INSERT/UPDATE/DELETE)会自动为涉及行上排他锁,无需手动指定。

    • 兼容性:与 S 锁、X 锁均互斥。

(三)特殊锁类型

  1. 元数据锁(MDL 锁)

    • 自动触发,无需手动操作,用于保护数据表的元数据(表结构、字段定义等)一致性,防止并发 DDL 与 DML 操作冲突。

    • 细分类型:

    • MDL 共享读锁:执行 SELECT、SHOW 等查询操作时自动获取,多个事务可同时持有;不阻塞其他事务的读操作,阻塞其他事务的 DDL 操作(ALTER/DROP TABLE 等)。

    • MDL 排他写锁:执行 ALTER、DROP、RENAME 等 DDL 操作时自动获取,仅允许一个事务持有;阻塞其他所有事务的读操作(SELECT)和写操作(DML/DDL)。

    • 注意:长时间运行的查询会持有 MDL 读锁,导致后续 DDL 操作阻塞,需避免慢查询占用 MDL 锁。

  2. Auto-Inc 锁

    • 针对自增列(AUTO_INCREMENT)的特殊表级锁,用于保证自增序列的唯一性和连续性,仅 InnoDB 引擎支持。

    • 锁释放机制:依赖 innodb_autoinc_lock_mode 配置(MySQL 5.1 及以上版本支持),分为 3 种模式:

    • 模式 0:仅使用 Auto-Inc 锁,插入语句执行期间持有表锁,语句结束后释放,并发插入性能最差。

    • 模式 1(默认):已知插入行数的操作(如 INSERT ... VALUES (),(),())使用轻量级互斥锁,插入完成即释放;未知插入行数的操作(如 INSERT ... SELECT)使用 Auto-Inc 锁,语句结束后释放,兼顾性能与自增序列一致性。

    • 模式 2:所有插入操作均使用轻量级互斥锁,插入完成即释放,并发插入性能最优,但可能导致主从复制中自增序列不一致(仅适用于主从同步为 row 格式的场景)。

  3. 谓词锁

    • 针对空间索引(如 GIS 数据的 SPATIAL 索引)的特殊锁类型,用于锁定空间数据的查询范围,防止并发修改导致数据不一致,仅 InnoDB 引擎支持空间索引时生效,日常业务中极少使用。

MySQL 事务的二阶段提交是什么?

(一)核心定义

MySQL 事务的二阶段提交是基于 Redo Log(重做日志)Binlog(二进制日志) 的一致性保障机制,主要用于解决主从复制场景下的数据一致性问题,确保 Redo Log 与 Binlog 日志内容完全同步,避免单机崩溃或主从同步异常导致数据丢失 / 不一致。

(二)完整执行流程

二阶段提交将事务的提交过程分为 准备阶段(Prepare Phase)提交阶段(Commit Phase),具体步骤如下:

  1. 事务执行阶段:执行 DML 操作(INSERT/UPDATE/DELETE),InnoDB 引擎将数据变更写入内存缓冲区(Buffer Pool),同时生成对应的 Redo Log 日志(仅写入 Redo Log Buffer,未刷盘)。

  2. 准备阶段(Prepare)

    • InnoDB 引擎将 Redo Log Buffer 中的日志刷写到磁盘(Redo Log 文件),并将 Redo Log 的事务状态标记为 prepare(准备完成)。

    • 此时事务并未真正提交,仅保证数据变更已持久化到 Redo Log,即使后续崩溃,也可通过 Redo Log 恢复数据。

  3. 提交阶段(Commit)

    • MySQL 上层(Server 层)将事务的变更记录写入 Binlog 日志,并将 Binlog 刷写到磁盘。

    • InnoDB 引擎读取 Binlog 中的事务 XID(全局唯一事务 ID),确认 Binlog 写入完成后,将 Redo Log 的事务状态从 prepare 修改为 commit(提交完成)。

    • 释放事务持有的所有锁资源,事务提交完成。

(三)崩溃恢复机制

当事务执行过程中(介于准备阶段与提交阶段之间)发生数据库崩溃,重启后 InnoDB 引擎会通过以下逻辑进行一致性恢复:

  1. 扫描磁盘上的 Redo Log 文件,筛选出状态为 prepare 的事务(未完成提交的事务)。

  2. 提取该事务的 XID,去 Binlog 日志中查询是否存在该 XID:

    • 若 Binlog 中存在该 XID:说明 Binlog 已成功写入,重启后将 Redo Log 中该事务的状态修改为 commit,完成事务提交,保证数据一致性。

    • 若 Binlog 中不存在该 XID:说明 Binlog 未写入完成,重启后回滚该事务,丢弃 Redo Log 中的数据变更,避免数据不一致。

(四)优化:日志组提交

为提升并发性能,MySQL 对 Redo Log 和 Binlog 均实现了 组提交(Group Commit) 优化:

  • 原理:将多个并发事务的日志写入操作批量合并,一次刷盘操作处理多个事务的日志,减少磁盘 I/O 次数(磁盘 I/O 是数据库性能瓶颈之一)。

  • 效果:大幅提升高并发场景下的事务提交效率,降低日志刷盘对数据库性能的影响。

MySQL 中如果发生死锁应该如何解决?

死锁是指两个或多个事务互相持有对方需要的锁资源,且互相等待对方释放锁,导致所有事务都无法继续执行的一种阻塞状态(仅发生在持有排他锁的场景下)。InnoDB 引擎提供了自动处理机制,同时也支持手动干预解决。

(一)自动处理机制

  1. 死锁检测机制(默认开启)

    • 依赖配置 innodb_deadlock_detect = ON(MySQL 5.7 及以上版本默认开启),InnoDB 引擎会实时检测事务之间的锁依赖关系,当发现循环依赖(死锁)时,会自动选择回滚代价最小的事务(通常是持有锁资源最少、执行步骤最少、影响数据量最小的事务),释放该事务持有的锁资源,打破死锁循环,让其他事务继续执行。

    • 优势:处理速度快,无需人工干预,适合大部分常规场景。

    • 局限:高并发场景下,死锁检测会消耗大量 CPU 资源,可能影响数据库性能。

  2. 锁等待超时机制

    • 依赖配置 innodb_lock_timeout = 50(默认 50 秒,单位:秒),当某个事务等待锁资源超过该超时时间时,InnoDB 引擎会自动回滚该事务,释放持有的锁资源。

    • 优势:作为死锁检测机制的补充,处理一些死锁检测未覆盖的阻塞场景。

    • 局限:超时时间固定(可手动调整),等待超时期间事务无法执行,影响业务响应速度;可能误回滚正常的长事务。

(二)手动干预方案

当自动处理机制无法快速解决死锁(如高并发场景、死锁频繁发生),或需要快速恢复业务时,可通过手动干预解决,步骤如下:

  1. 查询死锁信息

    • 方法 1:执行 SHOW ENGINE INNODB STATUS;,查看输出结果中的「LATEST DETECTED DEADLOCK」部分,获取死锁事务的 ID、持有锁、等待锁、执行 SQL 等关键信息。

    • 方法 2:查询 INFORMATION_SCHEMA 系统表,获取阻塞事务与锁资源信息:

    • 查询锁等待情况:SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

    • 查询当前持有锁:SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;

    • 查询当前事务:SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;

  2. 终止死锁事务

    • 根据上述查询结果,找到死锁循环中的关键事务(通常是回滚代价较小的事务),执行 KILL [事务ID]; 命令终止该事务,释放锁资源,打破死锁循环。

    • 注意:KILL 命令会强制回滚该事务,需确认该事务未执行关键业务操作,避免数据丢失或业务异常。

  3. 后续优化(避免死锁再次发生)

    • 统一事务操作顺序:所有事务对多个表 / 行的操作遵循相同的顺序,避免循环依赖。

    • 缩小事务粒度:尽量缩短事务执行时间,减少事务持有锁的时长,快速释放锁资源。

    • 合理创建索引:避免无索引导致行锁退化为表锁,减少锁冲突概率。

    • 调整隔离级别:将事务隔离级别从可重复读(RR)调整为读已提交(RC),减少间隙锁与临键锁的使用,降低死锁概率(需接受可能出现的幻读)。

    • 关闭死锁检测(高并发场景):若死锁极少发生,可执行 SET GLOBAL innodb_deadlock_detect = OFF; 关闭死锁检测,依赖锁等待超时机制处理,减少 CPU 消耗(需谨慎使用)。