沭阳住房和城乡建设局网站,网络服务,排名前十的室内设计公司,成立公司注册资本什么意思前言 数据库死锁问题是我们老生常谈的问题了#xff0c;在我们实际开发过程中经常会遇到#xff0c;为了尽量避免出现死锁#xff0c;我们需要了解出现死锁的场景。同时#xff0c;如果线上出现了死锁之后怎么去分析、排查和解决#xff0c;下面我就这两点介绍一下。 一、…前言 数据库死锁问题是我们老生常谈的问题了在我们实际开发过程中经常会遇到为了尽量避免出现死锁我们需要了解出现死锁的场景。同时如果线上出现了死锁之后怎么去分析、排查和解决下面我就这两点介绍一下。 一、数据库死锁介绍 1、什么是数据库死锁
数据库的死锁是指不同的事务在获取资源时相互等待导致无法继续执行的一种情况。当发生死锁时数据库系统会自动中断其中一个事务以解除死锁。在数据库中事务可以分为读事务和写事务。读事务只需要获取读锁而写事务需要获取写锁。当多个事务同时操作同一组数据时可能会引发死锁的出现。
2、MySQL 发生死锁的场景
2-1、事务同时更新多个表
当一个事务同时更新多个表并且使用了不同的顺序可能会导致死锁的发生。例如事务 A 首先更新表 X此时获取到了 X 表的锁并在未释放该锁的情况下尝试更新表 Y而事务 B 首先更新表Y此时获取到了 Y 表的锁并在未释放锁的情况下尝试更新表 X。这种情况下两个事务会相互等待对方的锁释放从而形成死锁。
2-2、事务嵌套
当一个事务内部开启了另一个事务并在内层事务中更新了某个表而外层事务也需要更新该表的同一行记录时就有可能发生死锁。因为外层事务需要等待内层事务释放锁而内层事务需要等待外层事务释放锁。
2-3、索引顺序不一致
当多个事务按照不同的顺序访问相同的数据行并且使用了不同的索引时可能会发生死锁。例如事务 A 按照索引 1 的顺序访问数据行事务 B 按照索引 2 的顺序访问同一组数据行这样两个事务之间就会产生死锁。
2-4、不同事务同时更新相同的索引
当多个事务同时更新相同的索引时可能会导致死锁。这是因为事务在更新索引时会获取对应的锁并在未释放锁的情况下尝试更新其他数据从而形成死锁。 二、解决死锁问题 如果线上发生了死锁我们应该采取以下步骤进行处理
1、 监控死锁
正常情况下我们都会建立死锁监控机制以便及时掌握死锁情况同时设置相应的预警机制以便在死锁发生时能够及时处理。
通过数据库的监控工具或命令可以查看是否存在死锁情况如果出现则了解死锁的具体情况包括死锁的事务和死锁的资源。
2、终止死锁事务
根据监控结果找到造成死锁的事务并手动选择其中一个事务终止。可以根据事务的执行时间、影响行数、优先级等因素进行终止决策。可以通过 select * from information_schema.innodb_trx 语句查看死锁情况。
在 innodb 中有三张表可以帮助我们更好去分析死锁信息
information_schema.innodb_trx事务信息表。information_schema.innodb_locks事务锁的信息表。information_schema.innodb_lock_waits锁等待关系表。
系统自动解除死锁
正常情况下当发生死锁时MySQL 系统会自动解除死锁至于解除哪个事务的锁需要亏了一个代价在解除死锁方面会选择回滚事务产生影响最小的一个进行回滚。
这里就要提一下两个概念了一个是事务的权重trx_weight另外一个是事务的调度权重trx_schedule_weight
事务的权重与回滚事务的选择有关。具体与事务 undo 版本链的长度有关回滚的 undo 记录越多产生的影响就会越大MySQL 就不会选择这样的事务倘若事务权重一样会选择事务等待队列等待时间短的事务进行回滚。事务的调度权重与事务获取资源的先后有关。MySQL8.0.20 之前在等待锁的事务优先级排序采取 FIFO 算法之后采取 CATS 算法。该算法通过分配调度权限对等待的事务进行优先级排序该权重是根据事务阻塞的事务数量计算的。例如两个事务正在等待同一对象上的锁那么阻塞最多事务的事务将被分配更大的调度权重如果权重相等则优先考虑等待时间最长的事务分配资源。
3、重试事务
终止死锁事务后需要重新执行被终止的事务。这可能需要一些逻辑处理例如对数据进行回滚或者重新执行一些操作。
4、分析死锁原因
通过数据库的日志和监控信息分析死锁的原因。下面是查看死锁日志的命令语句
show engine innodb status;分析死锁日志然后根据死锁原因对数据库的设计和代码进行优化以尽量减少死锁的发生。
同时也可以根据分析结果针对性地进行数据库结构调整、索引优化、事务隔离级别调整等措施以降低死锁的概率。
5、避免死锁建议
事务尽可能小不要将复杂逻辑放进一个事务里。涉及多行记录时约定不同事务以相同顺序访问。业务中要及时提交或者回滚事务可减少死锁产生的概率。表要有合适的索引。可尝试将隔离级别改为 ReadCommit 。