前言html
任何系统无论在什么阶段都须要关注生产环境错误日志,最近几个月内,发现偶尔会出现数据库死锁状况。之前碰到的数据库类错误大部分是SQL语法形成的错误,来到新东家以后才第一次碰到死锁状况,之前是搞游戏开发,如今是搞电商类开发,多是不一样的项目不一样的业务的缘由吧,查阅了各类资料后发现,是我想错了:(。通常业务瓶颈在数据库层,对于数据库层的问题须要重点关注,觉得死锁这种状况是很严重的问题,这个要分状况,偶尔死锁对业务不会有太大的影响,我又想错了:(。mysql
虫子发现
第一次发现死锁很惊讶,这个是什么鬼?不知道是什么缘由?不知道怎么解决?不知道会不会影响业务?没有搞清楚这个问题,生怕是业务的定时炸弹,天天内心不踏实。虫子发现以下:sql
死锁
对数据库记录操做以前,当前线程须要先请求得到相关锁,得到锁以后才会执行SQL语句。锁根据粒度分为表锁和行锁(不是全部存储引擎都支持行锁),锁根据类型分为排他锁和共享锁(不一样的操做须要得到的锁类型不一样,不一样的锁类型行为也不相同)。资源越少,出现死锁的几率会更小,好比只支持表锁的存储引擎会比支持行锁的存储引擎出现的几率更小。
并发事务出现时,当出现多个事务间彼此等待对方资源释放,事务因没有处理完,已经得到锁的资源不会释放,你们彼此这样等待着僵持着,这样会一直下去,死锁就这样产生了。
死锁现象关系型数据库没法避免,不是MySQL 数据库独有。MySQL 数据库会自动检查死锁状况,当发现时,会回滚更简单的事务并返回给线程一个错误。怎么判断哪一个事务更简单呢,根据事务影响的纪录行数判断,纪录行数越小被认为更简单。数据库
诊断分析
当出现死锁时,为了解决这个问题,须要诊断分析当时出现死锁的上下文信息以及相关的执行语句,这样才能知道怎么避免。MySQL 数据库只保存最近一次的死锁事务,若是同时有超过2个事务出现死锁,至多只保存2个事务信息。执行MySQL 客户端命令:SHOW ENGINE INNODB STATUS得到最近一次事务死锁相关信息。若是是MySQL 5.6以后的版本,能够打开全局变量innodb_print_all_deadlocks=ON,这样死锁相关信息会保存到MySQL 错误日志中。若是是不支持这个变量的版本,能够采用定时执行客户端命令采集死锁信息,而后保存到日志文件中,每一个事务都有惟一编号,能够根据这个去重。当死锁频繁出现时,须要引发注意;当偶尔出现时,能够不用关注。死锁信息格式以下,不一样MySQL 版本内容会有点差别,相关说明能够查看参考资料一。并发
预防措施
知道死锁为什么物,以及经过分析诊断日志信息,就能够对症下药了,可是有一些通用的基本原则能够遵照:
1 尽量保持事务简单以及快速执行;
2 尽量保持事务影响行数少以及涉及的相关表少;
3 避免使用外键约束,其实大部分应用容许数据冗余的;
4 影响行数大的事务尽可能使用相同的排序过滤;
5 数据库并发不高的业务,能够经过某种方式顺序执行语句,一次只执行一个,经过查资料知道一种实现方式:建立一个表,这个表始终只有一条纪录,各个事务经过争夺这个锁来实现线性执行,感受这个应该没啥用:(;
6 有些场景死锁会检查不到,设置锁的过时时间就很重要了,MySQL能够经过设置选项innodb_lock_wait_timeout;spa
危害程度
锁资源得不到及时释放,会影响数据库并发处理,若是常常出现死锁,须要及时采起措施处理,若是只是偶尔出现,业务逻辑捕捉到死锁错误能够采起重试执行事务,对业务不会有什么影响,总之具体问题须要具体分析:)线程
后记
一开始遇到这个问题有点紧张,觉得是很严重的问题,无从下手,不了解相关的背景知识嘛,看来人丑仍是要多读书啊(:(:3d
参考资料
【1】How to deal with MySQL deadlocks
https://www.percona.com/blog/2014/10/28/how-to-deal-with-mysql-deadlocks/
【2】deadlock
http://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_deadlock
【3】Deadlock Detection and Rollback
http://dev.mysql.com/doc/refman/5.7/en/innodb-deadlock-detection.html
【4】 How to Cope with Deadlocks
http://dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks.html日志