MySQL 5.7 存储过程报错不回滚
2020-05-26 本文已影响0人
G_izmooo
本人在做项目时,遇到了一个异常蛋疼的问题,困扰了很久。
项目原先的数据库选用的是Oracle,后来到新项目中,换成了MySQL,一些主要的业务操作都是由Procedure来实现完成的。之前写过SqlServer的存储过程没接触过MySQL的。开发过程中,一路顺畅,业务数据流转都很正常。等到部署给客户试用时,频繁的出现死锁、事务不回滚的情况。对于大批量的业务数据出错但不回滚,会持续的产生恶性循环。这篇文章主要用来记录下最近半年对项目维护过程中对存储过程的一些了解。
一、事务不回滚
贴下示例的存储过程
CREATE PROCEDURE procedure_test(
IN in_param1 VARCHAR(100),
IN in_param2 VARCHAR(20),
OUT out_return_code VARCHAR(100)
)
label:BEGIN
DECLARE r_param1 DECIMAL;
-- 定义异常
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1 @P1 = MYSQL_ERRNO,@P2 = MESSAGE_TEXT;
SET out_return_code = CONCAT('999||',@P1,'||',@P2);
ROLLBACK;
END;
START TRANSACTION;-- 开始事务
-- 业务逻辑
SET out_return_code = '******';
CALL procedure_getID('testType',out_return_code);
IF LEFT(out_return_code,3)<>'000' THEN
SET out_return_code ='获取事务ID失败';
ROLLBACK;
LEAVE label;
END IF;
--业务逻辑
COMMIT;-- 事务结束
END label
在客户试运行过程中,业务逻辑的sql发生了错误之后,发现存储过程退出了,但数据没有ROLLBACK。这就很恐怖了,首先补数据就很痛苦。经过了一段时间的测试,一个个业务逻辑代码删除,一条条SQL执行盘错。最终发现,在Procedure中调用另外的Procedure就会发生不回滚的情况。
procedure_getID 这个Procedure中也start transaciton + commit了,因此业务逻辑报错后,虽然调用了ROLLBACK但是procedure_getID的事务提交会影响到当前的Procedure,直接做了提交。后面将procedure_getID中的业务SQL拷贝至当前Procedure之后,发生错误之后就会回滚了。
二、死锁
这个问题目前还在解决中,这个死锁情况不多所以也就得过且过了。但是还是想先记录下,后面解决了再记录下来。目前有三个主要业务用到了一张数据表(t_details)。目前有时间了,准备着手处理这个问题,列出几点分析:
1,t_details索引建立过多,各个Procedure用到的时候没有合理查询条件,考虑用查询需要update/delete的数据的主键,再进行数据的CRUD操作。
2,其中行锁不直接锁记录,而是锁索引,如果一条SQL语句用到了主键索引,MySQL会锁住主键索引;如果一条语句操作了非主键索引,MySQL会先锁住非主键索引,在锁定主键索引。
3,将三个业务的对于t_details的操作拆分成三个业务任务表来分开操作,避免同时执行三个业务时抢资源的情况。
如果有不对的地方,请大佬提点下,多谢~