MySQL问题7

MySQL中的锁类型

1. 粒度锁

  • 行级锁

    • 针对数据表中的某一行或多行加锁
    • 并发度高,但开销较大
  • 表级锁

    • 针对整张表加锁
    • 并发度低,但开销小、加锁快

2. 意向锁

  • 意向锁

    • 用于表明事务即将在某些行上加共享锁或排他锁
    • 作用:快速判断表是否可以加表级锁

3. 读写锁

  • 共享锁(S 锁)

    • 允许事务读取数据,但不能修改
    • 多个事务可同时持有同一行的共享锁
  • 排他锁(X 锁)

    • 允许事务对数据进行读写
    • 其他事务不能再获取该数据的共享锁或排他锁

4. 元数据锁(MDL)

  • 元数据锁

    • 用于保护表的结构定义
    • 例如:防止 DDL 与 DML 并发冲突

5. InnoDB 特有锁

  • 间隙锁(Gap Lock)

    • 锁定索引记录之间的"间隙"
    • 防止其他事务在间隙中插入数据
  • 临键锁

    • 行锁 + 间隙锁的组合
    • 解决可重复读隔离级别下的幻读问题
  • 插入意向锁

    • 表示事务准备在某个间隙插入数据
    • 多个事务可以同时持有,不互相冲突
  • 自增锁

    • 针对自增字段的特殊锁
    • 保证多事务同时插入时自增值的唯一性和有序性

MySQL事务的二阶段提交

  • redo log:保证崩溃恢复(crash recovery),属于 InnoDB 层。
  • binlog:保证数据一致性和主从复制,属于 MySQL Server 层。

保证这两份日志的数据一致性,MySQL 引入了 二阶段提交

1、为什么需要二阶段提交?

只写一份日志,可能会出现以下问题:

  • 只写 redo log,不写 binlog

    → 主从复制时,从库无法得到这次事务。

  • 只写 binlog,不写 redo log

    → 主库宕机恢复后,数据丢失,但 binlog 里却存在这条记录,主从数据不一致。

必须保证 redo log 和 binlog 的写入结果要么同时成功,要么同时失败。

这就是 二阶段提交 的目的。


2、二阶段提交的流程

事务提交时,日志写入分为 prepare 阶段commit 阶段

  1. prepare 阶段

    • InnoDB 写入 redo log 的 prepare 状态,表示事务即将提交,但还没提交。
    • redo log 落盘,保证即使宕机,数据也能恢复。
  2. 写 binlog

    • MySQL Server 层写入 binlog。
    • 并将 binlog 刷盘。
  3. commit 阶段

    • InnoDB 把 redo log 状态从 prepare 改为 commit
    • 至此,事务提交完成。
  • 二阶段提交 = redo log + binlog 的双写机制
  • 作用 :保证事务在 崩溃恢复主从复制 中的一致性。
  • 关键点:先写 redo log(prepare)→ 写 binlog → redo log(commit)。

发生死锁的解决办法

1. 死锁的检测机制

  • 自动检测

    InnoDB 内部有 死锁检测机制 (默认开启),当检测到死锁时,会选择 回滚一个代价较小的事务,让另一个事务继续执行。

    • 参数:innodb_deadlock_detect=ON
  • 超时等待

    如果检测关闭,则依赖锁等待超时机制。

    • 参数:innodb_lock_wait_timeout(默认 50 秒)。
  • kill发生死锁的语句


2. 排查死锁的方式

  • 查看最近死锁日志

    sql 复制代码
    SHOW ENGINE INNODB STATUS\G
    • 发生死锁的事务 ID
    • 执行的 SQL
    • 加锁的情况
    • 被回滚的事务

常见的死锁日志

java 复制代码
------------------------
LATEST DETECTED DEADLOCK
------------------------
2025-09-08 21:05:45
*** (1) TRANSACTION:
TRANSACTION 12345, ACTIVE 3 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 5 lock struct(s), heap size 1136, 3 row lock(s)
MySQL thread id 25, OS thread handle 140146653595200, query id 1234 localhost root
UPDATE orders SET status='done' WHERE id=100;

*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 123 page no 456 n bits 72 index `PRIMARY` of table `test`.`orders` trx id 12345 lock_mode X locks rec but not gap
Record lock, heap no 5 PHYSICAL RECORD: n_fields 2; compact format; info bits 0

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 123 page no 456 n bits 72 index `PRIMARY` of table `test`.`orders` trx id 12345 lock_mode X locks rec but not gap waiting

*** (2) TRANSACTION:
TRANSACTION 12346, ACTIVE 2 sec fetching rows
mysql tables in use 1, locked 1
LOCK WAIT 5 lock struct(s), heap size 1136, 3 row lock(s)
MySQL thread id 26, OS thread handle 140146653595600, query id 1235 localhost root
UPDATE orders SET status='done' WHERE id=200;

*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 123 page no 456 n bits 72 index `PRIMARY` of table `test`.`orders` trx id 12346 lock_mode X locks rec but not gap
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 123 page no 456 n bits 72 index `PRIMARY` of table `test`.`orders` trx id 12346 lock_mode X locks rec but not gap waiting

*** WE ROLL BACK TRANSACTION (2)

3. 死锁的常见原因

  1. 事务访问顺序不一致

    事务 A 先锁表 1 再锁表 2,事务 B 先锁表 2 再锁表 1。

  2. 长事务

    占用大量行锁,导致其他事务长时间等待。

  3. 没有索引,导致锁范围扩大

扫描范围大 → 行锁变表锁或间隙锁,引发冲突。

  1. 外键约束或级联更新

父子表之间可能导致意料之外的加锁顺序。


4. 解决死锁的方法

(1)应用层面预防

  • 统一访问顺序
    保证不同事务访问同一组资源时,顺序一致。
  • 尽量减少锁范围
    通过索引精确过滤,避免无索引全表扫描加锁。
  • 缩短事务时间
    避免长事务,及时提交,减少锁占用。
  • 分解大事务
    把一个大事务拆成多个小事务提交。

(2)数据库配置层面

  • 启用死锁检测 (默认开启)
    让 InnoDB 自动选择一个事务回滚。
  • 合理设置超时时间
    innodb_lock_wait_timeout 调小,可以更快释放死锁等待。

(3)发生死锁后的处理

  • 应用重试机制
    由于 MySQL 会回滚一个事务,应用需要捕获 Deadlock found when trying to get lock 错误,然后 重试事务

相关推荐
五颜六色的池7 小时前
my sql 常用函数及语句的执行顺序
数据库·sql
Gold Steps.7 小时前
从 “T+1” 到 “秒级”:MySQL+Flink+Doris 构建实时数据分析全链路
大数据·数据库·数据分析
花北城7 小时前
【MySQL】Oracle与MySQL,跨库数据转储
数据库·mysql·oracle
一條狗7 小时前
学习日报 20250929|数据库与缓存一致性策略的选择
redis·mysql·kafka
没有bug.的程序员8 小时前
MySQL 配置调优参数:从基础到生产级优化指南
java·数据库·mysql·优化·mysql配置调优
optimistic_chen8 小时前
【Java EE进阶 --- SpringBoot】Mybatis操作数据库(基础)
数据库·经验分享·spring boot·笔记·spring·java-ee·mybatis
支付宝小程序云8 小时前
百宝箱开放平台 ✖️ 查询信息列表
数据库
对着晚风做鬼脸8 小时前
MySQL进阶知识点(六)---- 存储引擎
数据库·mysql
百锦再8 小时前
从 .NET 到 Java 的转型指南:详细学习路线与实践建议
android·java·前端·数据库·学习·.net·数据库架构
卷Java9 小时前
uni-app 模板语法修复说明
java·数据库·spring boot·uni-app·mybatis