Carry の Blog Carry の Blog
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • Python
  • Shell
  • MySQL8-SOP手册
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Carry の Blog

好记性不如烂键盘
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • Python
  • Shell
  • MySQL8-SOP手册
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • MySQL

    • MySQL8一键安装配置优化
    • MySQL导出CSV格式解决乱码
    • MySQL 角色管理
    • MySQL网络抓包审计
    • MySQL性能压测
    • MySQL配合Consul读写分离
    • Gh-ost重建表,清除表碎片率
    • MySQL MGR配合MySQL-router实现innodb-cluster
    • MySQL 快速分析binlog定位问题
    • MySQL执行计划分析
    • DBA常用SQL和命令整理备查
    • mysqldump实时同步数据
    • MySQL的事务隔离级别
    • MySQL存储过程批量生成数据
    • MySQL insert on duplicate key update,replace into , insert ignore的理解
    • MySQL不同字符集之间的区别和选择
    • MySQL为什么有时候会选错索引
    • MySQL死锁问题
      • 前言
      • 相关概念
        • 并发控制
        • 事务的特性
      • 死锁的定义与危害
        • 什么是死锁
        • 死锁的危害
      • 死锁的典型场景
        • 场景一:互相请求对方持有的锁
        • 场景二:间隙锁导致的死锁
      • 死锁的预防和处理
        • 预防措施
        • 死锁检测和处理
      • 总结
      • 参考资料
    • MySQL使用SQL语句查重去重
    • MySQLdump逻辑备份
    • MySQL主从跳过异常GITD
    • MySQL8设置slowlog记录所有语句
    • MySQL8快速克隆插件使用指南
    • MySQL8双1设置保障安全
    • MySQL锁
    • innodb cluster安装
    • MySQL里的left join 和right join以及inner join
    • optimize table和 analyze table的区别
    • MySQL 字段的区分度计算公式
    • MySQLReplicaSet 安装
    • 脚本实现MySQL ReplicaSet 高可用
    • MySQL 的Left join,Right join和Inner join 的区别
    • MySQL45讲学习笔记
  • Redis

  • Keydb

  • TiDB

  • MongoDB

  • Elasticsearch

  • Kafka

  • victoriametrics

  • BigData

  • Sqlserver

  • 数据库
  • MySQL
Carry の Blog
2022-03-12
目录

MySQL死锁问题

# MySQL死锁问题

# 前言

在日常开发中,我们经常会遇到数据库死锁问题。就像交通堵塞一样,当多个事务互相等待对方释放资源时,就形成了死锁。本文将围绕InnoDB存储引擎中的死锁问题,详细介绍相关概念、产生原因、典型场景以及解决方案。

# 相关概念

# 并发控制

并发控制(Concurrency Control)是数据库管理系统用于保证数据一致性的重要机制。

# 锁的类型

MySQL实现并发控制主要通过两种类型的锁:

  • 共享锁(Shared Lock,S锁):也叫读锁,允许多个事务同时读取同一资源
  • 排他锁(Exclusive Lock,X锁):也叫写锁,一个事务获取了写锁后,其他事务无法获取该资源的读锁或写锁

# 锁粒度

InnoDB存储引擎支持多种粒度的锁:

  • 表锁:锁定整张表,开销小但并发度低
  • 行锁:只锁定涉及的数据行,开销较大但并发度高
  • 间隙锁(Gap Lock):锁定索引记录之间的间隙,防止幻读
  • Next-Key Lock:行锁和间隙锁的组合

# 事务的特性

事务必须满足ACID特性:

  • 原子性(Atomicity):事务是不可分割的工作单位,要么全部执行,要么全部回滚
  • 一致性(Consistency):事务执行前后数据库必须保持一致状态
  • 隔离性(Isolation):事务执行过程中的中间状态对其他事务不可见
  • 持久性(Durability):事务一旦提交,其修改将永久保存在数据库中

# 事务隔离级别

MySQL支持四种事务隔离级别:

隔离级别 脏读 不可重复读 幻读
READ UNCOMMITTED 可能 可能 可能
READ COMMITTED 不可能 可能 可能
REPEATABLE READ(默认) 不可能 不可能 可能
SERIALIZABLE 不可能 不可能 不可能

# 死锁的定义与危害

# 什么是死锁

死锁是指两个或多个事务在执行过程中,因争夺资源而造成的一种互相等待的现象。比如:

  • 事务A持有资源1,等待资源2
  • 事务B持有资源2,等待资源1 这样两个事务就陷入了互相等待的死锁状态。

# 死锁的危害

  1. 事务无法完成,系统资源被长期占用
  2. 降低系统性能和吞吐量
  3. 可能引发连锁反应,导致更多事务被阻塞

# 死锁的典型场景

以下示例基于InnoDB存储引擎,隔离级别为REPEATABLE READ。

# 场景一:互相请求对方持有的锁

-- 准备数据
CREATE TABLE `test` (
  `id` int NOT NULL,
  `value` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

INSERT INTO test VALUES (1, 10), (2, 20);

-- Session A
START TRANSACTION;
UPDATE test SET value = 11 WHERE id = 1;

-- Session B
START TRANSACTION;
UPDATE test SET value = 21 WHERE id = 2;

-- Session A
UPDATE test SET value = 12 WHERE id = 2;  -- 等待Session B释放锁

-- Session B
UPDATE test SET value = 22 WHERE id = 1;  -- 死锁发生
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 场景二:间隙锁导致的死锁

CREATE TABLE `orders` (
  `id` int NOT NULL,
  `status` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_status` (`status`)
) ENGINE=InnoDB;

-- Session A
START TRANSACTION;
SELECT * FROM orders WHERE status = 1 FOR UPDATE;

-- Session B
START TRANSACTION;
INSERT INTO orders VALUES (5, 1);  -- 被Session A的间隙锁阻塞

-- Session A
INSERT INTO orders VALUES (6, 1);  -- 死锁发生
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 死锁的预防和处理

# 预防措施

  1. 按固定顺序访问表和行

    • 对多个表进行操作时,应该按照相同的顺序
    • 批量更新数据时,按主键或索引顺序进行更新
  2. 合理设计事务

    • 保持事务尽量短小
    • 一次性锁定所需要的所有资源
    • 避免事务中的用户交互
  3. 优化表结构和索引

    • 合理设计索引,避免全表扫描
    • 避免使用过多的锁定范围
  4. 应用层优化

    • 使用乐观锁替代悲观锁
    • 适当的重试机制
    • 合理设置事务隔离级别

# 死锁检测和处理

InnoDB有两种处理死锁的方式:

  1. 等待超时

    • 通过innodb_lock_wait_timeout参数设置
    • 默认值为50秒
  2. 死锁检测

    • 通过innodb_deadlock_detect参数控制
    • 发现死锁后,会回滚代价较小的事务
-- 查看和设置等待超时时间
SHOW VARIABLES LIKE 'innodb_lock_wait_timeout';
SET GLOBAL innodb_lock_wait_timeout = 50;

-- 查看死锁检测状态
SHOW VARIABLES LIKE 'innodb_deadlock_detect';
1
2
3
4
5
6

# 总结

死锁是并发数据库系统中难以完全避免的问题。通过:

  • 理解死锁产生的原因
  • 采用合理的预防措施
  • 正确配置数据库参数
  • 优化应用程序设计

我们可以最大限度地减少死锁的发生,提高系统的可用性和性能。

# 参考资料

  • 《高性能MySQL》第三版
  • MySQL官方文档:https://dev.mysql.com/doc/refman/8.0/en/innodb-deadlocks.html
上次更新: 4/24/2025

← MySQL为什么有时候会选错索引 MySQL使用SQL语句查重去重→

最近更新
01
tidb fast ddl
04-04
02
TiDB配置文件调优 原创
04-03
03
如何移除TiDB中的表分区 原创
04-03
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式