MySQL死锁

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 死锁检测和回滚如何处理死锁

欢迎订阅关注公众号:赵KK日常技术记录

MySQL死锁
MySQL死锁场景
查看最近一次死锁的日志
MySQL八种锁
1,行锁(Record Locks)
2,间隙锁(Gap Locks)
3,临键锁(Next-key Locks)
4,共享锁/排他锁(Shared and Exclusive Locks)
5,意向共享锁/意向排他锁(Intention Shared and Exclusive Locks)
6,插入意向锁(Insert Intention Locks)
7,自增锁(Auto-inc Locks)
死锁检测和回滚
如何处理死锁

官网:https://www.mysqlzh.com/doc/215/427.html#innodb-deadlock-detection

MySQL死锁

概念:mysql死锁是指两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。当多个事务试图以不同的顺序锁定资源时,就可能会产生死锁。多个事务同时锁定同一个资源时,也会产生死锁。
线程死锁:死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(线程)称为死锁进程(线程)。

MySQL死锁场景

1.在循环中删除
2.大事务等待
3.唯一索引冲突

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

查看最近一次死锁的日志

SHOW ENGINE INNODB STATUS;

执行此命令,出现type、name、status三列信息
由于satus列信息较多,则至展示部分信息

=====================================
2023-01-11 14:37:11 0x7fda4238f700 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 19 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 18476361 srv_active, 0 srv_shutdown, 281390 srv_idle
srv_master_thread log flush and writes: 18757594
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 34266326
OS WAIT ARRAY INFO: signal count 1461380308
RW-shared spins 0, rounds 938476348, OS waits 24858303
RW-excl spins 0, rounds 1272221568, OS waits 2060918
RW-sx spins 25186792, rounds 204022245, OS waits 262983
Spin rounds per wait: 938476348.00 RW-shared, 1272221568.00 RW-excl, 8.10 RW-sx
------------------------
LATEST FOREIGN KEY ERROR
------------------------
2022-12-24 11:44:18 0x7fda32f3d700  Cannot drop table `csx_b2b_scm`.`emp`
because it is referenced by `csx_b2b_scm`.`work`
------------------------
LATEST DETECTED DEADLOCK
------------------------

MySQL八种锁

行锁(Record Locks)

共享的(S)锁允许一个事务去读一行(tuple)。

独占的锁(X)允许一个事务更新或删除一行。

间隙锁(Gap Locks)

gap锁,又称为间隙锁。存在的主要目的就是为了防止在可重复读的事务级别下,出现幻读问题

临键锁(Next-key Locks)

innodb默认的锁就是Next-Key locks。
间隙锁在innoDB中的唯一作用就是在一定的“间隙”内防止其他事务的插入操作,以此防止幻读的发生:
防止间隙内有新数据被插入。
防止已存在的数据,更新成间隙内的数据。

共享锁/排他锁(Shared and Exclusive Locks)

共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
排他锁又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。

意向共享锁/意向排他锁(Intention Shared and Exclusive Locks)

意图共享(IS):事务T 意图给表T上单独的tuple设置S 锁定。

意图独占(IX):事务T 意图给这些tuple设置X 锁定。

插入意向锁(Insert Intention Locks)

普通的Gap Lock 不允许 在 (上一条记录,本记录) 范围内插入数据
插入意向锁Gap Lock 允许 在 (上一条记录,本记录) 范围内插入数据
插入意向锁的作用是为了提高并发插入的性能, 多个事务 同时写入 不同数据 至同一索引范围(区间)内,并不需要等待其他事务完成,不会发生锁等待

自增锁(Auto-inc Locks)

在InnoDB中,每个含有自增列的表都有一个自增长计数器。当对含有自增长计数器的表进行插入时,首先会执行select max(auto_inc_col) from t for update来得到计数器的值,然后再将这个值加1赋予自增长列。我们将这种方式称之为AUTO_INC Lock。

Next-Key锁定:避免幽灵问题
在行级锁定中,InnoDB 使用一个名为next-key locking的算法。InnoDB以这样一种方式执行行级锁定:当它搜索或扫描表的索引之时,它对遇到的索引记录设置共享或独占锁定。因此,行级锁定事实上是索引记录锁定。

InnoDB对索引记录设置的锁定也映像索引记录之前的“间隙”。如果一个用户对一个索引上的记录R有共享或独占的锁定,另一个用户 不能紧接在R之前以索引的顺序插入一个新索引记录。这个间隙的锁定被执行来防止所谓的“幽灵问题”。假设你想要从有一个标识符值大于100的子表读并锁定所有子记录,并想着随后在选定行中更新一些 列:

SELECT * FROM child WHERE id > 100 FOR UPDATE;
假设在id列有一个索引。查询从id大于100的第一个记录开始扫描。如果设置在索引记录上的锁定不把在间隙生成的插入排除在外,一个新行可能与此同时被插进表中。如果你在同一事务内执行同样的SELECT,你可能会在该查询返回的结果包里看到一个新行。这与事务的隔离原则是相反的:一个事务应该能够运行,以便它已经读的数据在事务过程中不改变。如果我们把一套行视为数据项,新的“幽灵”子记录可能会违反这一隔离原则。

当InnoDB扫描一个索引之时,它也锁定所以记录中最后一个记录之后的间隙。刚在前一个例子中发生:InnoDB设置的锁定防止任何插入到id可能大过100的表。

你可以用next-key锁定在你的应用程序上实现一个唯一性检查:如果你以共享模式读数据, 并且没有看到你将要插入的行的重复,则你可以安全地插入你的行,并且知道在读过程中对你的行的继承者设置的next-key锁定与此同时阻止任何人对你的 行插入一个重复。因此,the next-key锁定允许你锁住在你的表中并不存在的一些东西。

死锁日志读取

异常标题:数据库出现死锁
异常归属:小分队
异常产生:sys-2023-01-07 16:04:29
异常来源:DBA
异常等级:通知
异常内容:2023-01-07T16:04:29.645882+08:00 5462835 [Note] InnoDB: Transactions deadlock detected, dumping detailed information.
2023-01-07T16:04:29.645898+08:00 5462835 [Note] InnoDB: 
*** (1) TRANSACTION:

TRANSACTION 798960649, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 5462836, OS thread handle 139666471368448, query id 2121361009 10.0.xx.xxx otmst_app updating
DELETE FROM xxxx
 WHERE  xxx
2023-01-07T16:04:29.645925+08:00 5462835 [Note] InnoDB: *** (1) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 1182 page no 862042 n bits 256 index uniq_order_interface of table xxx trx id 798960649 lock_mode X locks rec but not gap waiting
Record lock, heap no 12 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
 0: len 14; hex 5a43323330313033303030353436; asc xxx;;
 1: len 30; hex 544d535f4d515f4c495354454e45525f434f4e5441494e45525f494e5445; asc xxx; (total 35 bytes);
 2: len 8; hex 8000000000517624; asc      Qv$;;

2023-01-07T16:04:29.646140+08:00 5462835 [Note] InnoDB: *** (2) TRANSACTION:

TRANSACTION 798960647, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 5462835, OS thread handle 139666346735360, query id 2121360071 10.0.xx.xxx otmst_app updating
DELETE FROM xxxx
 WHERE  xxx
2023-01-07T16:04:29.646162+08:00 5462835 [Note] InnoDB: *** (2) HOLDS THE LOCK(S):

RECORD LOCKS space id 1182 page no 862042 n bits 256 index uniq_order_interface of table xxx trx id 798960647 lock_mode X locks rec but not gap
Record lock, heap no 12 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
 0: len 14; hex 5a43323330313033303030353436; asc xxx;;
 1: len 30; hex 544d535f4d515f4c495354454e45525f434f4e5441494e45525f494e5445; asc xxx; (total 35 bytes);
 2: len 8; hex 8000000000517624; asc      Qv$;;

2023-01-07T16:04:29.646349+08:00 5462835 [Note] InnoDB: *** (2) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 1182 page no 862042 n bits 256 index uniq_order_interface of table xxx trx id 798960647 lock_mode X waiting
Record lock, heap no 12 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
 0: len 14; hex 5a43323330313033303030353436; asc xxx;;
 1: len 30; hex 544d535f4d515f4c495354454e45525f434f4e5441494e45525f494e5445; asc xxx; (total 35 bytes);
 2: len 8; hex 8000000000517624; asc      Qv$;;

2023-01-07T16:04:29.646526+08:00 5462835 [Note] InnoDB: *** WE ROLL BACK TRANSACTION (1)

事务一信息:
TRANSACTION 798960649:事务id
ACTIVE 0 sec: 活跃时间
starting index read:事务当前正在根据索引读取数据
mysql tables in use 1, locked 1:表示此事务修改了一个表,锁了一行数据。
MySQL thread id 5462836:线程id
query id 2121361009:查询id
10.0.xx.xxx otmst_app updating:数据库地址
RECORD LOCKS:行锁
index uniq_order_interface:使用唯一索引
lock_mode X locks,锁模式:排它锁。(X:排他锁,S:共享锁)
but not gap:非间隙锁
其中的0-2标识锁具体哪一行

事务二信息:
3 lock struct(s):表示该事务的锁链表的长度为3,每个链表节点代表该事务持有的一个锁结构,包括表锁,记录锁以及 autoinc 锁等。heap size 1136 为事务分配的锁堆内存大小。
RECORD LOCKS:共享记录锁
WE ROLL BACK TRANSACTION (1)
事务1回滚

两个事务对同一条记录进行修改,两条事务均为X锁,故只允许一个事务更新,但此时事务1并未执行完成,X锁未释放,事务2进行更新需要等待事务1释放
死锁是事务型数据库典型的问题,但是除非它们频繁出现以至于你更本不能运行某个事务,它们一般是不危险的。正常地,你必须编写你的应用程序使得它们总是准备如果因为死锁而 回滚一个事务就重新发出一个事务。

InnoDB使用自动行级锁定。即使在只插入或删除单个行的事务的情况下,你可以遇到死锁。这是因为这些操作不是真正的“极小的”,它们自动对插入或删除的行的(可能是数个)索引记录设置锁定。

你可以用下列技术对付死锁减少它们发生的可能性:

· 用Use SHOW INNODB STATUS来确定最后一个死锁的原因。这样可以帮助你调节应用程序来避免死锁。

· 总是准备着重新发出事务,如果它因为死锁而失败了。死锁不危险,再试一次。

· 经常提交你的事务。小事务更少地倾向于冲突。

· 如果你正使用锁定读,(SELECT ... FOR UPDATE或 ... LOCK IN SHARE MODE),试着用更低的隔离级别,比如READ COMMITTED。

· 以固定的顺序访问你的表和行。则事务形成良好定义的查询并且没有死锁。

· 添加精心选定的索引到你的表。则你的查询需要扫描更少的索引记录并且因此设置更少的锁定。使用EXPLAIN SELECT来确定对于你的查询,MySQL认为哪个索引是最适当的。

· 使用更少的锁定。如果你可以接受允许一个SELECT从一个旧的快照返回数据,不要给它添加FOR UPDATE或LOCK IN SHARE MODE子句。这里使用READ COMMITTED隔离级别是比较好的,因为每个在同一事务里的持续读从它自己新鲜的快照里读取。

· 如果没有别的有帮助的了,用表级锁定系列化你的事务。用LOCK TABLES对事务型表(如InnoDB)的正确方法是设置AUTOCOMMIT = 0 并且不调用UNLOCK TABLES直到你明确地提交了事务。例如,如果你需要写表t1并从表t读,你可以按如下做:

· SET AUTOCOMMIT=0;
· LOCK TABLES t1 WRITE, t2 READ, ...;
· [do something with tables t1 and t2 here];
· COMMIT;
· UNLOCK TABLES;
表级锁定使得你的事务很好地排队,并且死锁被避免了。

· 领一个系列化事务的方法是创建一个辅助的“semaphore” 表,它只包含一个单行。让每个事务在访问其它表之前更新那个行。以这种方式,所有事务以序列的方式发生。注意,InnoDB即时死锁检测算法也能在这种情况下起租用,因为系列化锁定是行级锁定。超时方法,用MySQL表级锁定,必须被用来解决死锁。

· 在应用程序中使用LOCK TABLES命令,如果AUTOCOMMIT=1,MySQL不设定InnoDB表锁定。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
2月前
|
SQL 关系型数据库 MySQL
遇到mysql数据库死锁,你会怎么排查?
遇到mysql数据库死锁,你会怎么排查?
108 0
|
5天前
|
监控 关系型数据库 MySQL
MySQL锁机制与解决死锁问题
MySQL锁机制与解决死锁问题
20 5
|
2月前
|
存储 SQL 关系型数据库
深入MySQL锁机制:原理、死锁解决及Java防范技巧
深入MySQL锁机制:原理、死锁解决及Java防范技巧
|
2月前
|
SQL JavaScript 关系型数据库
Mysql索引不当引发死锁问题
本文通过真实案例解析了MySQL在高并发环境下出现死锁的问题。数据库表`t_award`包含多个索引,但在执行特定SQL语句时遭遇索引失效,导致更新操作变慢并引发死锁。分析发现,联合索引`(pool_id, identifier, status, is_redeemed)`因`identifier`允许为空值而导致索引部分失效。此外,`pool_id`上的普通索引产生的间隙锁在高并发下加剧了死锁风险。为解决此问题,文中提出了调整索引顺序至`(pool_id, status, is_redeemed, identifier)`等方案来优化索引使用,进而减轻死锁现象。
|
2月前
|
Oracle 关系型数据库 MySQL
Mysql和Oracle数据库死锁查看以及解决
【8月更文挑战第11天】本文介绍了解决MySQL与Oracle数据库死锁的方法。MySQL可通过`SHOW ENGINE INNODB STATUS`查看死锁详情,并自动回滚一个事务解除死锁;也可手动KILL事务。Oracle则通过查询V$LOCK与V$SESSION视图定位死锁,并用`ALTER SYSTEM KILL SESSION`命令终止相关会话。预防措施包括遵循ACID原则、优化索引及拆分大型事务。
|
2月前
|
监控 关系型数据库 MySQL
MySQL死锁是什么
【8月更文挑战第26天】MySQL死锁是指两个或多个事务在执行过程中,因争夺锁资源而造成的相互等待的现象,若无外力干涉,它们都将无法继续执行。这种相互等待的情况会导致整个系统陷入停滞状态,影响数据库的性能和稳定性。
43 0
|
3月前
|
SQL 存储 关系型数据库
细说 MySQL 死锁
死锁检查在MySQL 8.0中涉及三个主要步骤:构造锁等待图、初始化事务权重和提升权重。首先,当事务进入锁等待状态时,信息会被记录到内存中的`waiting_threads`,形成快照数组。接着,对这个数组进行排序,构造出锁等待图,表示事务间的等待关系。然后,初始化所有等待事务的权重为1,如果一个事务在其他事务等待后进入等待,其权重会被提升,以避免长时间等待。最后,根据锁等待图,提升那些同时阻塞其他事务的权重,但不包括参与死锁的事务。权重更新后,死锁检查线程将依据这些信息来检测和解决死锁。
71 15
|
3月前
|
SQL 存储 关系型数据库
细说 MySQL 死锁
【7月更文挑战第26天】MySQL 死锁
31 4
|
3月前
|
SQL 算法 关系型数据库
(十)全解MySQL之死锁问题分析、事务隔离与锁机制的底层原理剖析
经过《MySQL锁机制》、《MySQL-MVCC机制》两篇后,咱们已经大致了解MySQL中处理并发事务的手段,不过对于锁机制、MVCC机制都并未与之前说到的《MySQL事务机制》产生关联关系,同时对于MySQL锁机制的实现原理也未曾剖析,因此本篇作为事务、锁、MVCC这三者的汇总篇,会在本章中补全之前空缺的一些细节,同时也会将锁、MVCC机制与事务机制之间的关系彻底理清楚。
|
3月前
|
缓存 监控 关系型数据库
MySQL PXC 集群死锁分析案例
前不久一个系统死锁导致部分业务受到影响,今次补上详细的节点日志分析过程。
61 1
下一篇
无影云桌面