最新消息:觉得本站不错的话 记得收藏哦 博客内某些功能仅供测试 讨论群:135931704 快养不起小站了 各位有闲钱就打赏下把 My Email weicots#gmail.com Please replace # with @

解决MYSQL死锁之路

LINX-SQL ajiang-tuzi 7278浏览

我遇到死锁的处理方式无非就是判断返回是的状态是否为死锁或者失败或者掉线,失败则直接抛出异常触发事务,进行回滚。否则重新尝试业务逻辑提交,成功则进行业务流程失败则抛出异常进行回滚。当然我用乐观锁多一点,但是在一些关键业务上我更喜欢用户悲观锁 ,毕竟谁也不想关于钞票的事情会出现错误。
至于为什么要写这篇博文,因为最近几天忙着做给顾客对接B2C系统基本都是写RPC适配然后忘得差不多了还有就是这些问题基本都是DBA朋友在做,所以赶紧去扒几个DBA朋友的博客重新复习一下。

为什么会出现死锁

并发控制是与数据库一起使用的概念,可确保并发执行数据库事务而不会破坏数据完整性。
关于此概念及其实现方法有很多理论和不同方法,但是我们将简要介绍MySQL(使用InnoDB时)处理它的方式,以及在高并发系统中可能出现的常见问题:死锁。
这些引擎通过使用称为MVCC(多版本并发控制)的方法来实现并发控制。在此方法中,当更新项目时,所做的更改不会覆盖原始数据,而是将创建该项目的新版本(包含更改)。因此,我们将存储该项目的多个版本。
该模型的主要优点之一是为查询(读取)数据而获取的锁与为写入数据而获取的锁不冲突,因此读取永远不会阻止写入,而写入永远不会阻止读取。
但是,如果存储了同一项目的多个版本,那么交易将看到哪个版本?为了回答这个问题,我们需要回顾事务隔离的概念。事务指定隔离级别,该级别定义一个事务必须与其他事务进行的资源或数据修改相隔离的程度,该程度与事务生成的锁定直接相关,因此可以在事务中指定级别,它可以确定一个正在运行的事务对其他正在运行的事务的影响。
这是一个非常有趣且冗长的主题,尽管我们不会在此博客中介绍太多细节。我们建议使用MySQL官方文档以进一步阅读该主题。
那么,在处理死锁时为什么要进入上述主题?因为sql命令将自动获取锁以确保MVCC行为,并且获取的锁类型取决于定义的事务隔离。
有几种类型的锁(关于MySQL的另一个漫长而有趣的主题),但是,关于锁的重要一点是锁之间如何相互作用(最准确地说,是如何发生冲突)。
这是为什么?因为两个事务不能同时在同一对象上持有冲突模式的锁。一个非次要的细节,一旦获得,通常会一直持有锁直到交易结束。
简单一点就是
一个死锁在MySQL发生在两个或多个事务相互持有并请求锁,创建依赖的循环。在交易系统中,死锁是生活中不可或缺的事实,并非完全可以避免的。InnoDB自动检测事务死锁,立即回滚事务并返回错误。它使用一个指标来选择最容易回滚的事务。尽管不必担心偶尔出现死锁,但频繁发生的事件需要引起注意。
在MySQL 5.6之前,只能使用SHOW ENGINE INNODB STATUS命令查看最新的死锁。但是,使用Percona Toolkit的pt-deadlock-logger,您可以按给定的时间间隔从SHOW ENGINE INNODB STATUS中检索死锁信息,并将其保存到文件或表中以进行后期诊断。有关使用pt-deadlock-logger的更多信息,请参阅本文。使用MySQL 5.6,您可以启用一个新变量innodb_print_all_deadlocks,使InnoDB中的所有死锁都记录在mysqld错误日志中。
在进行所有诊断之前,最重要的做法是让应用程序捕获死锁错误(MySQL错误编号1213)并通过重试事务来处理它。

我在MySQL中造成一个死锁给大家一个直观的例子
lock_dff

CREATE TABLE `table1` ( `id` INT NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL, `marks` INT NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB;
INSERT INTO table1 (id, name, marks) VALUES (1, "abc", 5);
INSERT INTO table1 (id, name, marks) VALUES (2, "xyz", 1);

第一窗

BEGIN;
UPDATE table1 SET marks=marks-1 WHERE id=1; -- X lock acquired on 1

第二视窗
再次更新

BEGIN;
UPDATE table1 SET marks=marks+1 WHERE id=2; -- X lock acquired on 2
UPDATE table1 SET marks=marks-1 WHERE id=1; -- LOCK WAIT!

第一个窗口(续)

UPDATE table1 SET marks=marks+1 WHERE id=2; -- DEADLOCK!
COMMIT;

我推荐去读这个作者的博文我就不重复写啦
解决死锁之路 – 了解常见的锁类型
解决死锁之路(终结篇) – 再见死锁

我以前遇到的一些mysql 问题

MYSQL 大并发情况 出现 Cannot assign requested address 问题 以及基础的TCP/IP TIME_WAIT状态原理解析
PHP Mysql数据库 长链接 短链接 (连接池 ?)

mysql-随机查询优化

下面是一些不一样的资料

中文资料
MySQL存储引擎--MyISAM与InnoDB区别
mysql(innodb)事务和锁
多版本并发控制(MVCC)在分布式系统中的应用
快速理解聚集索引和非聚集索引
MySQL死锁案例分析(一) FOR DBA
mysql 幻读的详解、实例及解决办法
浅析乐观锁与悲观锁

英文资料
Deadlocks in InnoDB (MySQL 8.0参考手册-InnoDB中的死锁)
How to Minimize and Handle Deadlocks(MySQL 8.0参考手册-如何最小化和处理死锁)
Understanding Deadlocks in MySQL & PostgreSQL(了解MySQL和PostgreSQL中的死锁)
How to deal with MySQL deadlocks(如何诊断MySQL死锁)
Deadlock and lock wait timeouts on medium to high traffic magento site. Not sure what to do.(高流量的magento网站上的死锁和锁定等待超时。不知道该怎么办)

案例
收集一些常见的 MySQL 死锁案例
由一次线上问题带来的MySQL死锁问题分析

magento 2死锁解决方案 (eBay->Permira->Adobe) 竟然卖给Adobe了

MAGENTO 2死锁重试模块
MAGENTO 1死锁重试模块

如何避免MySQL死锁
DBA
考虑到上面我们了解的死锁发生原因,您可以看到在数据库方面我们可以做很多事情来避免死锁。无论如何,作为DBA,我们有责任实际捕获它们,对其进行分析并向开发人员提供反馈。
实际上,这些错误是每个应用程序所特有的,因此您需要一个一个地检查它们,并且没有指南告诉您如何解决此问题。记住这一点,您可以找一些东西。
搜索长时间运行的事务。由于锁通常保持到事务结束,所以事务越长,对资源的锁定时间就越长。如果可能,请尝试将长期运行的事务拆分为较小/较快的事务。
有时实际上不可能拆分事务,因此工作应集中于每次尝试以一致的顺序执行那些操作,因此事务形成了定义明确的队列且不会死锁。
您还可以建议的一种解决方法是,将重试逻辑添加到应用程序中(当然,首先尝试解决根本问题),这样,如果发生死锁,应用程序将再次运行相同的命令。
检查使用的隔离级别,有时您可以通过更改它们来尝试。查找诸如SELECT FOR UPDATE和SELECT FOR SHARE之类的命令,
因为它们会生成显式锁,并评估是否确实需要它们,或者您可以使用较旧的数据快照。如果无法删除这些命令,
可以尝试的一件事是使用较低的隔离级别,例如READ COMMITTED。
当然,请始终将精选的索引添加到表中。然后,您的查询需要扫描更少的索引记录,因此设置了更少的锁。
在较高的级别上,作为DBA,您可以采取一些预防措施来最大程度地减少锁定。为了命名一个示例
,您可以避免在将添加列的同一命令中添加默认值。更改表将获得真正的锁定,并为其设置默认值实际上将更新具有空值的现有行,
从而使该操作花费了很长时间。因此,如果您将此操作拆分为几个命令,添加列,添加默认值,更新空值,则可以最大程度地减少锁定影响。
当然,DBA会从实践中获得很多类似的技巧(同时创建索引,在添加pk之前分别创建pk索引,依此类推),
但是重要的是学习和理解这种“思维方式”。 ”,并始终将我们正在执行的操作的锁定影响最小化。
简点理解
–更改应用程序。在某些情况下,可以通过将一个长事务分成较小的事务来大大减少死锁的发生,因此锁会更快地释放。在其他情况下,死锁上升是因为两个事务以一个不同的顺序接触一个或多个表中的同一组数据。然后更改它们以相同的顺序访问数据,换句话说,将访问序列化。这样,当事务同时发生时,您将拥有锁等待而不是死锁。
–更改表架构,例如删除外键约束以分离两个表,或添加索引以最小化扫描和锁定的行。
–在间隙锁定的情况下,可以将事务隔离级别更改为读取已提交的会话或事务以避免它。但是,会话或事务的二进制日志格式必须是ROW或MIXED。

转载请注明:(●--●) Hello.My Weicot » 解决MYSQL死锁之路

蜀ICP备15020253号-1