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

相关推荐
吃饭最爱5 小时前
JUnit技术的核心和用法
数据库·oracle·sqlserver
专注API从业者5 小时前
Python/Java 代码示例:手把手教程调用 1688 API 获取商品详情实时数据
java·linux·数据库·python
雨落Liy6 小时前
SQL 函数从入门到精通:原理、类型、窗口函数与实战指南
数据库·sql
Kt&Rs7 小时前
MySQL复制技术的发展历程
数据库·mysql
小小菜鸡ing7 小时前
pymysql
java·服务器·数据库
手握风云-7 小时前
MySQL数据库精研之旅第十六期:深度拆解事务核心(上)
数据库·mysql
boonya8 小时前
Redis核心原理与面试问题解析
数据库·redis·面试
沙二原住民8 小时前
提升数据库性能的秘密武器:深入解析慢查询、连接池与Druid监控
java·数据库·oracle
三毛20048 小时前
玳瑁的嵌入式日记D33-0908(SQL数据库)
jvm·数据库·sql