网络公司网站官网,贵阳企业网站建设制作,大连网页制作培训,程序员公司最近#xff0c;笔者在查看线上服务日志时#xff0c;发现spring大量异常#xff0c;异常中都显示了同样的报错信息#xff0c;信息如下。Deadlock found when trying to get lock; try restarting transaction调研之后发现是mysql发生了死锁#xff0c;这也是笔者第一次遇… 最近笔者在查看线上服务日志时发现spring大量异常异常中都显示了同样的报错信息信息如下。Deadlock found when trying to get lock; try restarting transaction调研之后发现是mysql发生了死锁这也是笔者第一次遇到数据库死锁问题详细研究后将过程记录为文章以便日后参考回顾。1. 死锁死锁指的是两个或两个以上的进程(线程)在执行的时候因为争夺资源出现相互等待的一种现象。产生死锁需要同时满足以下四个条件互斥条件:一个资源每次只能一个进程使用不可抢占进程1在获取资源使用的过程中进程2不能抢占进程1正在使用的资源占有且等待进程在申请资源的时不能释放已经持有的资源循环等待:进程1等待进程2资源同时进程2等待进程1持有的资源出现循环等待的情况2. 死锁日志为了查找造成死锁的sql语句笔者通过show engine innodb status查看到最近的一次死锁日志通过这个sql语句我们就能确定造成死锁的事务//事务一相关信息
*** (1) TRANSACTION:
TRANSACTION 50E, ACTIVE 66 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 376, 2 row lock(s)
MySQL thread id 7, OS thread handle 0x27448, query id 82 localhost 127.0.0.1 root Updating
//当前事务正在执行的sql语句
update tb1 set c1 10 where id 5
//以下信息记录了锁等待信息
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
//正在申请主键索引行记录的x锁
RECORD LOCKS space id 0 page no 3328 n bits 72 index PRIMARY of table test.tb1 trx id 50E lock_mode X locks rec but not gap waiting
Record lock, heap no 5 PHYSICAL RECORD: n_fields 5; compact format; info bits 0//事务2相关信息
*** (2) TRANSACTION:
TRANSACTION 50F, ACTIVE 47 sec starting index read
mysql tables in use 1, locked 1
3 lock struct(s), heap size 376, 2 row lock(s)
MySQL thread id 8, OS thread handle 0x277e4, query id 83 localhost 127.0.0.1 root Updating
update tb1 set c1 10 where id 5
//正在持有的锁主键索引为5的行记录级别的S锁
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 0 page no 3328 n bits 72 index PRIMARY of table test.tb1 trx id 50F lock mode S locks rec but not gap
Record lock, heap no 5 PHYSICAL RECORD: n_fields 5; compact format; info bits 00: len 4; hex 80000005; asc ;;1: len 6; hex 00000000050d; asc ;;2: len 7; hex 8b00000d080110; asc ;;3: len 4; hex 80000005; asc ;;4: len 4; hex 80000005; asc ;;*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 3328 n bits 72 index PRIMARY of table test.tb1 trx id 50F lock_mode X locks rec but not gap waiting
Record lock, heap no 5 PHYSICAL RECORD: n_fields 5; compact format; info bits 00: len 4; hex 80000005; asc ;;1: len 6; hex 00000000050d; asc ;;2: len 7; hex 8b00000d080110; asc ;;3: len 4; hex 80000005; asc ;;4: len 4; hex 80000005; asc ;;WE ROLL BACK TRANSACTION (2)笔者发现死锁日志最后一行出现了WE ROLL BACK TRANSACTION (2)信息。这是Mysql innodb引擎自动检测死锁机制MySQL选择打断其中一个事务破坏死锁条件来消除死锁。Mysql官方文档上显示mysql会选择杀死小的事务这里的小指的是执行的insert,update,detected语句数目小的事务。需要注意的是mysql innodb死锁检测只能针对innodb引擎级别死锁innodb死锁检测不能检测到应用层级别死锁3. 死锁复现为了避免泄露公司的业务和数据笔者在开发环境复现了这个死锁表结构如下所示事务执行顺序如下为什么这个sql语句会造成死锁呢原因如下4. 解决方案通过上文的死锁复现笔者发现事务2和事务3的作用一样的他们都操作了相同的资源执行事务2和事务3可能是同一进程内的线程执行也可能是位于不同进程的线程执行。针对这种情况笔者在生产环境使用分布式锁解决这种死锁场景笔者以操作的行记录id作为分布式资源id关于分布式锁可以参考笔者先前写的文章5. 发现死锁上述场景Mysql发现了死锁并选择回滚其中一个事务解决了死锁问题但是老版本的mysql没有死锁检测机制如果出现死锁连接可能都会处于等待状态直到50S的锁等待超时这会长时间占据数据库连接导致数据库连接池连接耗尽tomcat无法获取数据库连接一直处于等待状态随后tomcat队列排满后整个服务就会处于僵死状态在生产环境中是非常大的事故。如果发现mysql执行SQL语句长时间不响应。我们可以通过show full processlist 命令查看当前所有数据库连接状态如果连接在等待锁资源在State状态会显示waiting for table metadata lock信息同时可以通过info信息查看数据库连接执行了哪一条sql语句如果所有的等待锁的连接执行的sql语句都涉及到了同一张表那么就能断定哪站表发生了死锁6. 查找连接知道哪张表被锁定仍然无法解决我们的问题我们需要知道哪个数据库连接对表加了锁才能kill连接从mysql5.5开始information_schema增加了三个关于锁的表通过这三张表我们能够找到连接id6.1 innodb_locks这张表提供了各个事务请求的数据库锁但是仍然没有获取的数据库锁。这张表提供的最重要的信息是请求锁的事务id6.2 innodb_trx通过这张表我们能查到当前innodb引擎执行的所有事务id以及当前执行事务的数据库连接id于是便能通过kill命令杀死数据库连接更关键的是我们能查找到事务正在执行的sql语句6.3 innodb_lock_waits这张表中requesting_trx_id代表了申请锁资源的事务IDrequesting_lock_id代表申请的锁idblocking_trx_id代表了阻塞事务70E的事务idblocking_lock_id代表了阻塞事务70E的锁的ID出现死锁后我们可以通过innodb_lock_waits获取相互等待的事务id通过事务id从innodb_trx查找到数据库连接id然后使用kill杀死连接7. 应用层死锁innodb_locksinnodb_trxinnodb_lock_waits三张表只能查找到innodb引擎层死锁的数据库连接id对于server层死锁就无能为力了。比如说下述命令加的锁lock tables tb1 write;
flush tables with read lock;针对这种死锁无法通过杀死数据库连接id达到释放锁的目的针对这种情况笔者尚未找到解决方案在生产环境下笔者也遇到过这种情况造成的死锁笔者只能寻求DBA重启数据库可以暂时解决死锁问题但是最好的解决方案是尽量不要使用手动加锁命令。