MySQL · 2016-10-15 0

Percona-Toolkit系列之pt-deadlock-logger数据库死锁记录利器

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等等相关信息。是不是超棒的功能呢。

PT-LOCK

抓取到的信息我们可以输出到标准输出,也可以放到日志文件中或者数据库的表中,具体放到哪里就看你的需求和条件了。下面我们就看看具体安装使用

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锁。

 

20161015002849

这是可以发现开着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的输出(点击缩略图看高清大图):

20161015122827

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