1.死锁
关于死锁,相信计算机专业的同学不止在一门课里面接触过,操作系统老师讲过死锁吧,数据库系统概念的老师也讲过死锁吧。至于死锁原理,这里不赘述了,有兴趣的同学可以参考下这两篇博文(http://hedengcheng.com/?p=844,http://www.cnblogs.com/LBSer/p/5183300.html)写的非常棒,也推荐大家去看看《操作系统:精髓与设计原理(中文第七版)》第六章 6.1 死锁原理。本文纯粹讲述如何使用pt-deadlock-logger这个工具来记录死锁。
有同学可能有疑问了,pt-deadlock-logger仅仅只有一个死锁记录功能,这有啥用啊?又不能避免死锁,又不能打开死锁。赫赫,这么讲吧,当线上系统出现死锁,那是相当严重的事情,业务积压,各种领导都蹦出来了,数据库咋回事,各种运维围到你身边,这是比较严重事情哦,但是死锁场景又很难抓出来,让开发修改。但是pt-deadlock-logger就帮我们解决了这个问题。
2.pt-deadlock-logger
pt-deadlock-logger其实就是一个脚本连接到数据库服务器,用于探测死锁,一旦有死锁发现,就会从数据库中采集死锁相关信息,输出到你指定的地方。只要有死锁,我们就可以看到死锁SQL,死锁的线程ID等等相关信息。是不是超棒的功能呢。
抓取到的信息我们可以输出到标准输出,也可以放到日志文件中或者数据库的表中,具体放到哪里就看你的需求和条件了。下面我们就看看具体安装使用
3.pt-deadlock-logger使用
3.1准备一个账号
grant all privileges on *.* to 'pt'@'%' identified by 'pt';
权限等根据自己需求调整。
3.2死锁场景模拟&&pt-deadlock-logger首秀
CREATE TABLE lock_test(c1 INT, c2 VARCHAR(32), c3 VARCHAR(32), c4 VARCHAR(32), PRIMARY KEY(c1), KEY(c2)) ENGINE=innodb; /*插入测试数据*/ insert into lock_test values(1,'b','c','d'); insert into lock_test values(2,'ss','d','sd'); insert into lock_test values(3,'j','tt','ff'); insert into lock_test values(4,'tt','ww','rr');
pt-deadlock-logger开始检测数据库
[mysql@hpc02 ~]$ pt-deadlock-logger h=192.168.56.103,u=pt,p=pt
开两个会话开始模拟死锁case,为了简单明了,我们直接在主键上加记录锁,避免复杂的gap锁和next锁。
这是可以发现开着pt-deadlock-logger的终端已经输出了死锁的相关信息,信息非常详尽,是不是超棒。
[mysql@hpc02 ~]$ pt-deadlock-logger h=192.168.56.103,u=pt,p=pt server ts thread txn_id txn_time user hostname ip db tbl idx lock_type lock_mode wait_hold victim query 192.168.56.103 2016-10-14T23:24:52 6 0 20 root localhost test lock_test PRIMARY RECORD X w 0 select * from lock_test where c1=2 for update 192.168.56.103 2016-10-14T23:24:52 7 0 13 root localhost test lock_test PRIMARY RECORD X w 1 select * from lock_test where c1=1 for update
4.pt-deadlock-logger的输出
pt-deadlock-logger的输出非常详尽,完全可以帮我定位死锁之问题所在。我们来看看上面这个demo的输出(点击缩略图看高清大图):
server:数据库服务器地址,即死锁产生的数据库主机
ts:检测到死锁的时间戳
thread:产生死锁的线程id,这个id和show processlist里面的线程id是一致的
txn_id:innodb的事务ID
txd_time:死锁检查到前,事务执行时间
user:执行transcation的用户名
hostname:客户端主机名
ip:客户端ip
db:发生死锁的DB名
tbl:死锁发生的表名
idx:产生死锁的索引名(在上面这个demo里面, 我们直接走的主键,加的记录锁)
lock_type:锁的类型(记录锁,gap锁,next-key锁)
lock_mode:锁模式(S,X)
wait_hold:是否等着锁释放,一般死锁都是两个wait
victim:该会话是否做了牺牲,终止了执行
query:造成死锁的SQL语句
5.pt-deadlock-logger的常用配置文件
5.1将检查到的死锁信息输出到日志文件
#login to MySQL server# host=192.168.56.103 port=3306 #socket=/data/mysqldata1/mysql.sock user=pt password=pt charset=utf8 #log to file tab log=/home/mysql/pt.log daemonize interval=5
[mysql@hpc02 ~]$ pt-deadlock-logger --config pt-deadlock.cnf
tab:代表,输出是带上制表符,格式化显示,更加清晰
log:指定日志文件用来存储死锁检测信息输出
daemonize:放到后台运行,守护进程模式
interval:每隔5秒检测一次
还有几个参数没有列出
run-time:pt-deadlock-logger运行时间
iterations:pt-deadlock-logger检测的次数
假如--run-time 2m --iterations 4 --interval 30,运行两分钟,每隔30s检测一次,一共检测四次
5.2将检查到的死锁信息存储到数据库中
/*conf file*/ #login to MySQL server# host=192.168.56.103 port=3306 #socket=/data/mysqldata1/mysql.sock user=pt password=pt charset=utf8 #where to store
命令行指定目标数据库用于存储日志信息
pt-deadlock-logger --config pt-deadlock_tab.cnf --dest h=192.168.56.103,u=root,p=111111,P=3307,D=test,t=deadlocks --create-dest-table
表中存储的日志信息和上文demo一致,不再赘述
5.3将检查到的死锁信息输出到标准输出中
#login to MySQL server# host=192.168.56.103 port=3306 #socket=/data/mysqldata1/mysql.sock user=pt password=pt charset=utf8 #log to file tab
如下方式运行即可:
[mysql@hpc02 ~]$ pt-deadlock-logger --config pt-deadlock.cnf