死锁排查
INFORMATION_SCHEMA提供对数据库元数据的访问、关于MySQL服务器的信息,如数据库或表的名称、列的数据类型或访问权限。其中有一个关于InnoDB数据库引擎表的集合,里面有记录数据库事务和锁的相关表。
MySQL有关事务和锁的四条命令:
-
SELECT * FROM information_schema.INNODB_TRX;
命令是用来查看当前运行的所有事务。 -
SELECT * FROM information_schema.INNODB_LOCKs;
命令是用来查看当前出现的锁。 -
SELECT * FROM information_schema.INNODB_LOCK_waits;
命令是用来查看锁等待的对应关系。 -
show engine innodb status \G;
命令是用来获取最近一次的死锁信息。
在查询结果中可以看到是否有表锁等待或者死锁。如果有死锁发生,可以通过
KILL trx_mysql_thread_id
来杀掉当前运行的事务。
查询事务与锁的命令行
死锁是并发系统中常见的问题,同样也会出现在数据库MySQL的并发读写请求场景中。当两个及以上的事务,双方都在等待对方释放已经持有的锁或因为加锁顺序不一致造成循环等待锁资源,就会出现“死锁”。常见的报错信息为"Deadlock found when trying to get lock..."
MySQL死锁问题排查的常见思路:
-
通过多终端模拟并发事务,复现死锁。
-
通过上面四条命令,查看事务与锁的信息。
-
通过explain可以查看执行计划。
发生死锁异常后,通过开启InnoDB的监控机制来获取实时的死锁信息,它会周期性(每隔 15 秒)打印 InnoDb 的运行状态到 mysqld服务的错误日志文件中。
InnoDB的监控较为重要的有标准监控(Standard InnoDB Monitor)和锁监控(InnoDB Lock Monitor),通过对应的系统参数可以将其开启。
set GLOBAL innodb_status_output=ON;开启标准监控
set GLOBAL innodb_status_output_locks;开启所监控
另外,MySQL 提供了一个系统参数innodb_print_all_deadlocks
专门用于记录死锁日志,当发生死锁时,死锁日志会记录到 MySQL 的错误日志文件中。
另外,MySQL 提供了一个系统参数innodb_print_all_deadlocks
专门用于记录死锁日志,当发生死锁时,死锁日志会记录到 MySQL 的错误日志文件中。
如何尽可能避免死锁
-
合理的设计索引,区分度高的列放到组合索引前面,使业务SQL尽可能通过索引定位更少的行,减少锁竞争。
-
尽量按主键/索引去查找记录,范围查找增加了锁冲突的可能性,也不要利用数据库做一些额外额度计算工作。比如有的程序会用到
select … where … order by rand();
这样的语句,类似这样的语句用不到索引,因此将导致整个表的数据都被锁住。 -
大事务拆小。大事务更倾向于死锁,如果业务允许,将大事务拆小。
-
以固定的顺序访问表和行。比如两个更新数据的事务,事务A更新数据的顺序为1,2;事务B更新数据的顺序为2,1。这样更可能会造成死锁。
-
降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。
注意:本文归作者所有,未经作者允许,不得转载