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 错误,然后 重试事务

相关推荐
2603_9547083120 分钟前
全维度容错设计,打造微电网安全运行屏障
服务器·网络·数据库·人工智能·分布式·安全
IvorySQL1 小时前
从双解析器到循环工程:IvorySQL 五年技术演进路线的深度观察
大数据·数据库·人工智能·postgresql·开源
wefg11 小时前
【MySQL】事务
数据库·mysql
黑白极客1 小时前
mysql的高可用性
数据库·mysql
一十九的酒1 小时前
Oracle 12c 标准版无缝升级企业版实战记录
数据库·oracle·标准版切换到企业版
ZhengEnCi1 小时前
S02-SpringBoot实体类新增字段对已有数据的影响及自动DDL同步机制详解
数据库·spring boot
妙码生花2 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(三十二):增加 admin_rule 模型及数据表迁移
数据库·go·ai编程
千维百策6663 小时前
SRE 中如何消除琐务:提升工程效率与服务可靠性的实践方法
数据库
卓怡学长3 小时前
w266基于spring boot + vue 圣地延安美食乐享系统
java·数据库·vue.js·spring boot·spring·intellij-idea
名不经传的养虾人4 小时前
从0到1:企业级AI项目迭代日记 Vol.65|最危险的故障不是崩溃,是悄悄换掉了正确答案
数据库·人工智能·ai编程·ai-agent·企业ai