webgame中Mysql Deadlock ERROR 1213 (40001)错误的排查历程(四)

2014-11-24 11:43:47 · 作者: · 浏览: 1
= '53016' AND `id` = 9234 /*!*/; # at 511751090 #121128 15:59:46 server id 1 end_log_pos 511751240 Query thread_id=122679850 exec_time=0 error_code=0 SET TIMESTAMP=1354089586/*!*/; UPDATE `role_state` SET `pet` = 0, `pet_level` = 0 WHERE `role_id` = '53016' /*!*/; # at 511751240 #121128 15:59:46 server id 1 end_log_pos 511751885 Query thread_id=122679850 exec_time=0 error_code=0 SET TIMESTAMP=1354089586/*!*/; REPLACE INTO `role_fight` (`role_id`, `life_max`, `mana_max`, `attack_physical`, `attack_internal`, `defend_physical`, `defend_internal`, `dodge_rate`, `critical_rate`, `hit_rate`, `speed`, `defend_physical_plus`, `defend_internal_plus`, `dodge_level`,*****) VALUES ('53016', 4967, 3291, 350, 174, 518, 254, 500, 300, 9500, 913, 668, 668, 261, 700, 97, 133, 40.9, 34, *****) /*!*/; # at 511751885 #121128 15:59:46 server id 1 end_log_pos 511751912 Xid = 7457663579 COMMIT/*!*/;

OK,事务2的SQL语句全部找齐了。那么事务1的呢?如何找?

根据php的异常报错,确定主要包含的语句SQL信息,以及程序跟踪的代码行数,根据代码逻辑去确定该事务的所有SQL语句。再去BINLOG中找到该用户该业务的类似BINLOG:



# at 511805324
#121128 15:59:53 server id 1  end_log_pos 511805403 	Query	thread_id=122562823	exec_time=0	error_code=0
SET TIMESTAMP=1354089593/*!*/;
BEGIN
/*!*/;
# at 511805403
#121128 15:59:53 server id 1  end_log_pos 511805560 	Query	thread_id=122562823	exec_time=0	error_code=0
use xxx_roles_xxx/*!*/;
SET TIMESTAMP=1354089593/*!*/;
UPDATE `role_fight` SET `last_update_life` = '1354089587'
WHERE `role_id` = '53016'
/*!*/;
# at 511805560
#121128 15:59:53 server id 1  end_log_pos 511805695 	Query	thread_id=122562823	exec_time=0	error_code=0
SET TIMESTAMP=1354089593/*!*/;
UPDATE `role_state` SET `state` = 1
WHERE `role_id` = '53016'
/*!*/;
# at 511805695
#121128 15:59:53 server id 1  end_log_pos 511805889 	Query	thread_id=122562823	exec_time=0	error_code=0
use xxx_roles_xxx/*!*/;
SET TIMESTAMP=1354089593/*!*/;
DELETE FROM `queue_combats_update_roles`
WHERE `combat_id` = 'f27d62dad8efcaeb04cd8f5d7c0424db'
AND `role_id` = '53016'
/*!*/;
# at 511805889
#121128 15:59:53 server id 1  end_log_pos 511805916 	Xid = 7457670215
COMMIT/*!*/;

(请勿过于纠结上面binlog的thread_id跟show engine innodb status的thread_id一致的问题,这是因为我们程序是常驻进程,mysql连接不断开,不销毁,故一致了。而且,此日志是程序发现死锁之后,被mysql回滚之后,又重新提交的事务,算是不同时间点的相同事务)

案情还原:
根据案发现场的两个MYSQL INNODB事务的全部SQL语句,以及形成MYSQL INNODB 死锁的原因(感谢DBA组大雄哥的纠正),我们大约可以这么还原案情:

事务1:
UPDATE `role_fight` SET `last_update_life` = ’1354089587′ WHERE `role_id` = ’53016′
UPDATE `role_state` SET `state` = 1 WHERE `role_id` = ’53016′

事务2:
UPDATE `role_state` SET `pet` = 0, `pet_level` = 0 WHERE `role_id` = ’53016′
REPLACE INTO `role_fight` (`role_id`, `life_max`, `mana_max`, `attack_physical`, `attack_internal`,****) VALUES (’53016′, 4967, 3291, 350, 174, ***)

这四条语句构成了本次事务死锁的全部原因。
执行顺序肯定如下:

时间点 事务1 事务2 备注
1 begin
2 begin
3 UPDATE `role_state` SET `pet` = 0, `pet_level` = 0 WHERE `role_id` = ’53016′ 事务2 给 role_state表 role_id 53016记录上 X 锁
4 UPDATE `role_fight` SET `last_update_life` = ’1354089587′ WHERE `role_id` = ’53016′ 事务1 给 role_fight表 role_id 53016记录上 X 锁
5 REPLACE INTO `role_fight` (`role_id`, `life_max`, `mana_max`, `attack_physical`, `attack_internal`,****) VALUES (’53016′, 4967, 3291, 350, 174, ***) 这里是重点,事务2给role_fight表role_id的记录上 X 锁,发现被其他人(事务