如何解决MySQL死锁(看懂MySQL锁日志)

news2024/11/15 15:38:35

有时候系统在生产运行着,会突然爆出

[40001][1213] Deadlock found when trying to get lock; try restarting transaction

这个时候每个人都会很紧张,因为死锁会影响DB性能,严重时甚至拖垮整个系统。在实际的环境中,很多服务会共用一个数据库,一旦数据库挂了,基本就是P0事故。

那么,死锁发生时,我们如何定位到死锁发生的SQL?

死锁排查

实操前置准备

磨刀不误砍柴功,我们先准备下实验环境。

首先创建一张表:

create table users(
    id int comment "id",
    age int comment '年龄',
    id_no int comment '身份证号',
    UNIQUE KEY `uk_task_obj` (`id_no`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='用户表';

写入数据:

insert into users values (1,18,1);
insert into users values (2,18,2);
insert into users values (3,18,3);

接着,我们开两个事务:

事务一:

begin;
select * from users where id_no=1 for update;
select * from users where id_no=3 for update ;
commit;

事务二:

begin;
select * from users where id_no=3 for update ;
select * from users where id_no=1 for update ;
commit ;

可以看到,两个事务执行的语句都一样,只不过顺序不一样,我们按照以下时序去执行时,终端会提示Deadlock found when trying to get lock; try restarting transaction

事务一事务二

beigin;

select * from users where id_no=1 for update;

begin;

select * from users where id_no=3 for update ;
select * from users where id_no=3 for update ;
select * from users where id_no=1 for update ;(此时死锁发生)

查看日志

要定位死锁发生的原因,我们需要知道,是哪些事务持有了哪些锁,哪些事务又互相阻塞。

我们可以通过

SHOW ENGINE INNODB STATUS;

来查看死锁发生时的日志

日志分析

当执行SHOW ENGINE INNODB STATUS时,MySQL返回如下日志:

日志内容很多,我们主要关注:LATEST DETECTED DEADLOCK 这一部分。我们逐步讲解下这一部分的日志。

日志解释

LATEST DETECTED DEADLOCK

------------------------

2024-03-18 21:07:00 0x16be8b000

*** (1) TRANSACTION:

TRANSACTION 1836, ACTIVE 10 sec starting index read

TRANSACTION 1836:1836代表事务id;

active 10 sec:表示活跃时间

LOCK WAIT 4 lock struct(s), heap size 1128, 3 row lock(s)

MySQL thread id 17, OS thread handle 6121680896, query id 2044 localhost 127.0.0.1 root statistics

不重要,忽略

/* ApplicationName=GoLand 2023.2.2 */ select * from users where id_no=3 for update

这个事务执行的sql语句

*** (1) HOLDS THE LOCK(S):

RECORD LOCKS space id 2 page no 5 n bits 72 index uk_task_obj of table `test`.`users` trx id 1836 lock_mode X locks rec but not gap

Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 0

 0: len 4; hex 80000001; asc     ;;

 1: len 6; hex 000000000200; asc       ;;

HOLDS THE LOCK:表示当前事务持有的锁。

RECORD LOCKS:表明是记录锁

space id 2 page no 5 n bits 72 :这是mysql底层存储的位置,我们可以不理解。

index uk_task_obj of table `test`.`users` trx id 1836 lock_mode X locks rec but not gap:表明是索引uk_task_obj上的锁。X锁代表互斥锁,rec but not gap表示是记录锁不是间隙锁。

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 2 page no 5 n bits 72 index uk_task_obj of table `test`.`users` trx id 1836 lock_mode X locks rec but not gap waiting

Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0

 0: len 4; hex 80000003; asc     ;;

 1: len 6; hex 000000000202; asc       ;;

WAITING FOR THIS LOCK TO BE GRANTED:这句话就说了,这个事务在等待索引uk_task_obj上的一个记录锁,下面是等待的锁信息。

*** (2) TRANSACTION:

TRANSACTION 1837, ACTIVE 7 sec starting index read

mysql tables in use 1, locked 1

LOCK WAIT 4 lock struct(s), heap size 1128, 3 row lock(s)

MySQL thread id 18, OS thread handle 6120566784, query id 2058 localhost 127.0.0.1 root statistics

/* ApplicationName=GoLand 2023.2.2 */ select * from users where id_no=1 for update

第一个事务在等待锁,这个时候提到第二个事务了。事务id是1837,执行的语句是select * from users where id_no=1 for update

*** (2) HOLDS THE LOCK(S):

RECORD LOCKS space id 2 page no 5 n bits 72 index uk_task_obj of table `test`.`users` trx id 1837 lock_mode X locks rec but not gap

Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0

 0: len 4; hex 80000003; asc     ;;

 1: len 6; hex 000000000202; asc       ;;

1837事务持有的锁也是记录锁,也是在唯一索引uk_task_obj上。

锁的信息如下:

0: len 4; hex 80000003; asc     ;;

 1: len 6; hex 000000000202; asc       ;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 2 page no 5 n bits 72 index uk_task_obj of table `test`.`users` trx id 1837 lock_mode X locks rec but not gap waiting

Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 0

 0: len 4; hex 80000001; asc     ;;

 1: len 6; hex 000000000200; asc       ;;

WAITING FOR THIS LOCK TO BE GRANTED:等待锁

Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 0

 0: len 4; hex 80000001; asc     ;;

 1: len 6; hex 000000000200; asc

等待一个记录锁,可以看到,事务1836和1837持有和等待的锁是相反的,因而发生死锁

*** WE ROLL BACK TRANSACTION (2)

MySQL选择回滚第二个事务

常见的MySQL死锁场景

上面的例子,是最常见的由于select for update产生的死锁问题,以下还有几种发生死锁的场景。

批量update

例如:insert into users values(1,2,3),(2,2,3),(3,3,3)和insert into users values(3,2,3),(2,2,3),(1,3,3)同时执行时,会由于锁的冲突会导致死锁。表面是看insert into users values(1,2,3),(2,2,3),(3,3,3)是一条语句,实际上MySQL并不是一次性加完全部锁,它会按照SQL的书写顺序逐步加锁。解决方法是在批量插入之前,我们按一定规则排序,只要两条sql按相同的顺序加锁便不会有死锁问题

update退化为共享锁

在MySQL中,update语句加的是排它锁,也就是X锁。如果此时另外一个事务正在执行select语句,对同一个目标加了共享锁之后,执行update的事务会由于加X锁失败,转而变为共享锁。

此时会发生如下情况:

事务1执行完第一步之后,已经持有A的共享锁;

事务2执行第二步,由于排它锁加锁失败,转为持有A的共享锁,同时等待事务1释放共享锁;

事务3执行第三步,要将共享锁升级为排它锁,等待事务2释放共享锁。

此时事务1和2发生了循环等待,导致死锁发生。

总结:

  1. 通过SHOW ENGINE INNODB STATUS查看MySQL死锁日志
  2. select for update是最常见的死锁场景
  3. 批量update时注意加锁顺序、小心update的排它锁退化成共享锁导致死锁发生

诚意满满系列每一篇都是精挑细选,从大众知识点到原理再到具体实现,争取把一个知识点从头到尾完整讲下来,足以应付面试与工作。让读者读完之后能够有一种:“这个知识我看这一篇就够了”的感觉是本系列最大愿望。

对于本人而言,在之前的学习中也发现,八股文讲得细致但不系统,而系统的学习往往又宽泛不细致,所以也打算取长补短,互相结合一下,欢迎大家收藏关注,持续更新。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1528307.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JMeter压测SpringBoot项目

压力测试架构图如下: 配置JMeter 在JMeter的bin目录,双击jmeter.bat 新建一个测试计划,并右键添加线程组: 进行配置 一共会发生4万次请求。 ctrl + s保存; 添加http请求: 配置http请求: 配置断言,来判断当前请求是否成功: 正常响应如下:

EtherCAT开源主站 IGH 介绍及主站伺服控制过程

目录 前言 IGH EtherCAT主站介绍 主要特点和功能 使用场景 SOEM 主站介绍 SOEM 的特点和功能 SOEM 的使用场景 IGH 主站 和 SOEM对比 1. 功能和复杂性 2. 资源消耗和移植性 3. 使用场景 EtherCAT 通信原理 EtherCAT主站控制伺服过程 位置规划模式 原点复归模式…

金融知识分享系列之:ATR指标

金融知识分享系列之:ATR指标 一、ATR指标二、指标原理三、海龟交易法应用1.计算开仓数量2.制定止损/加仓规则 四、ATR指标总结 一、ATR指标 利用ATR指标计算仓位,设置止损的规则名称:平均真实波动幅度参数:(默认14)组成&#xff…

CMeet系列技术生态沙龙---《探索未来:生成式AI赋能千行百业·杭州》

当前数字化浪潮下,生成式AI技术正成为推动产业升级、提升竞争力的关键力量。为深入探索未来AI技术的赋能作用,促进技术生态的繁荣与发展,CSDN-CMeet系列沙龙活动旨在搭建一个交流与探索的平台,通过分享前沿研究成果和应用案例&…

OpenCV实现OCR图片文本检测

一、操作步骤 把左边这样的图片,通过仿射变换转换成右边那样的图片。 然后再通过pytesseract读取图片内容得到图片中的文本就好了。 需要使用到: 仿射变换ocr识别 注:本文使用现成图片,轮廓检测较为明显,若是自己拍…

Css 样式记录

实现两栏布局如图所示样式&#xff1a; 方法1&#xff1a; <div style"display: flex; justify-content: space-between; width:100%;"> <div>1</div> <div>2</div> </div> 方法2&#xff1a; <div style"display: fle…

pytest ui自动化

chromedriver.exe 要对应已安装的chrome版本号

大数据面试题 —— Zookeeper

目录 ZooKeeper 的定义ZooKeeper 的特点ZooKeeper 的应用场景你觉得Zookeeper比较重要的功能ZooKeeper 的选举机制 ***zookeeper主节点故障&#xff0c;如何重新选举&#xff1f;ZooKeeper 的监听原理 ***zookeeper集群的节点数为什么建议奇数台 ***ZooKeeper 的部署方式有哪几…

C语言种sizeof()和strlen的区别

sizeof 是 C 语言内置的操作符关键字&#xff0c;而 strlen 是 C 语言库函数&#xff1b; sizeof 仅用于计算数据类型的大小或者变量的大小&#xff0c;而 strlen 只能以结尾为 \0 的字符串作为参数&#xff1b; 编译器在编译时就计算出了 sizeof 的结果&#xff0c;而 strlen …

02_设计模式

文章目录 设计模式设计模式分类UML类图设计模式的原则 常用设计模式创建型设计模式单例设计模式饿汉模式懒汉模式&#xff08;线程不安全&#xff09;懒汉模式&#xff08;线程安全&#xff09;- Synchronized懒汉模式&#xff08;线程安全&#xff09;- Double Check懒汉模式&…

Android 系统源码快速入门

Android源码快速入门 今天分享的内容是Android源码快速入门&#xff0c;主要分为以下几个步骤&#xff1a; * 硬件要求 * 虚拟机安装 * 开发环境搭建 * 下载编译源码 * 从一个简单的实际开发需求体验 Framework 开发硬件要求 用于 Android Framework 开发的电脑需要较强的 C…

算法沉淀——贪心算法二(leetcode真题剖析)

算法沉淀——贪心算法二 01.最长递增子序列02.递增的三元子序列03.最长连续递增序列04.买卖股票的最佳时机 01.最长递增子序列 题目链接&#xff1a;https://leetcode.cn/problems/longest-increasing-subsequence/ 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子…

【第十一章】改进神经网络学习方式-Softmax

我们将主要使用交叉熵成本来解决学习减速的问题。然而&#xff0c;我想简要描述另一种解决方法&#xff0c;基于所谓的 softmax 层神经元。在本章的剩余部分&#xff0c;我们实际上不会使用 softmax 层&#xff0c;因此如果你时间紧迫&#xff0c;可以跳到下一节。然而&#xf…

Vue3 依赖注入provide与inject

简介 关于provide与inject下面是vue官网上的一些介绍 通常情况下&#xff0c;当我们需要从父组件向子组件传递数据时&#xff0c;会使用props。想象一下这样的结构&#xff1a;有一些多层级嵌套的组件&#xff0c;形成了一颗巨大的组件树&#xff0c;而某个深层的子组件需要一个…

【Flask开发实战】防火墙配置文件解析(二)之shell读取内容

一、前言 上一篇文章中&#xff0c;介绍了防火墙配置文件包含的基本元素和格式样式&#xff0c;并模拟了几组有代表性的规则内容&#xff0c;作为基础测试数据。在拿到基础测试数据后&#xff0c;关于我们最终想解析成的数据是什么样式的&#xff0c;其实不难看出&#xff0c;…

GateWay路由规则

Spring Cloud GateWay 帮我们内置了很多 Predicates功能&#xff0c;实现了各种路由匹配规 则&#xff08;通过 Header、请求参数等作为条件&#xff09;匹配到对应的路由 1 时间点后匹配 server:port: 8888 spring:application:name: gateway-servicecloud:nacos:discovery:…

四.排序(冒泡/选择)

目录 11-排序介绍 常见排序算法: 12-冒泡排序介绍 代码要求: 思路: 13-冒泡排序 代码: 14-选择排序 简单写法: 好的写法: 11-排序介绍 排序&#xff1a;将一组“无序”的记录序列调整为“有序”的记录序列。 列表排序&#xff1a;将无序列表变为有序列表 输入&#…

C++第七弹---类与对象(四)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、拷贝构造函数 1.1、概念 1.2、特征 2、运算符重载 2.1、等号运算符重载 总结 1、拷贝构造函数 1.1、概念 在现实生活中&#xff0c;可能…

【C++】手撕AVL树

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;能直接手撕AVL树。 > 毒鸡汤&#xff1a;放弃自…

Java安全基础 必备概念理解

Java安全基础 关键概念汇总 文章目录 Java安全基础 关键概念汇总前置知识1.构造器this以及包的使用2.继承3.重写/ 重载 / super4.多态5.区分和equals方法6.toString的使用7.Object的概念8.static,final,代码块static代码块final 9.动态代理10.类的动态加载1)类加载器含义&#…