记一次MYSQL存储过程调试过程
···
以下存储过程在最近总是偶尔出现返回失败的情况,由于看不到异常信息,所以查找原因颇费周折。
1 | DELIMITER $$ |
存储过程写的非常复杂,最主要的一点是发生异常时直接回滚,返回OUT_RESULT=0,无法得知异常信息,所以首要的是先给存储过程加上异常处理。下边是一段存储过程异常处理的方式,直接插入数据库表中,当然实际生产时,除了要插入异常信息,还要和相关的业务信息进行关联。其于以下的方式对存储过程进行改造,改造后的就不帖了。
1 | DROP PROCEDURE IF EXISTS proc_exception; |
因为失败是偶尔出现的,后来手动执行没啥问题,初步怀疑是并发问题。然后使用JMETER进行压力测试,第一次使用,还真是挺好用的。当进行20个用户发并调用存储过程时,只有第一个成功,其他都失败了。。。。。。,好惨啊!
查看proc_error_log表中的记录数据,显示
|code|message|
|—|—|
|1|2|
|40001|Deadlock found when trying to get lock; try restarting transaction|
|40001|Deadlock found when trying to get lock; try restarting transaction|
|40001|Deadlock found when trying to get lock; try restarting transaction|
|40001|Deadlock found when trying to get lock; try restarting transaction|
|40001|Deadlock found when trying to get lock; try restarting transaction|
|…|…|
好吧,死锁问题,使用命令 SHOW ENGINE INNODB STATUS 查看日志,查找死锁原因。(其他参考命令SHOW STATUS LIKE ‘innodb_row_lock%’;SHOW GLOBAL VARIABLES LIKE ‘innodb_lock_wait_timeout%’;)日志如下:
第一次压测后获取的日志:
1 | ------------------------ |
第二次压测后获取的日志:
1 | LATEST DETECTED DEADLOCK |
其可以确定是这条语句出现死锁,但是出现顺序暂时没分析明白,明天再更新。
1 | SELECT COUNT(*) INTO V_CVT FROM production_log_t WHERE task_no=IN_TASKNO AND (`status`='0' OR `status`='1') FOR UPDATE |