mysql存储过程花费10几个小时_原本预期1小时的发布,为何最终发布加校验实际花费时间5小时...

这是一个简单的数据生产导入的故事,原本故事情节应该是这样的:数据整理-->测试验证-->生产发布-->生产验证,然后就是各回各家,所以这本来应该是一个平淡的故事,然而实际却变成了如下情节:数据整理-->测试验证-->生产发布-->生产验证-->校验失败(预期数据未导入)-->问题排查-->解决问题-->生产发布-->生产验证-->校验问题(大部分数据是正确的,少部分数据不正确)-->问题排查(当时未能排查出原因,但能判断出异常与生产原有的几条异常数据有关)-->异常数据删除sql编写-->测试校验-->生产发布-->生产校验-->重新导入删除部分数据(异常数据这次直接排除,没包括在导入范围)-->部分异常数据请示领导修正-->修正Sql准备-->测试校验-->生产发布-->修正数据对应数据导入-->生产校验!

你以为到这里就结束了?NO NO NO,故事怎么可能就这么结束,因为这批数据导入有对应的其它业务,还需要执行该部分业务,最终确认后才能各回各家,结果发现,坑爹的数据库数据是修正了,但因为程序采用了Redis,异常数据还在Redis中,所以还要在Redis中删除该部分异常数据,还好程序部分对此有处理,直接删除没导致程序功能异常,至此本次发布才算结束,但此时也已经是凌晨0点了,这真是一个悲剧的故事……

首先需要介绍下本次导入的猪脚,一个预先写好,且已经发布至生产的存储过程,另外该猪脚所在场景是MySql,其大致代码精简后如下

1 DROP PROCEDURE IF EXISTS`usp_SadEvent`;2 DELIMITER $$3 CREATE PROCEDURE`usp_SadEvent`4 (5 IN identityNo VARCHAR(20),6 IN uName VARCHAR(15),7 IN cAmount LONG

8 )9 label_at_start:10 BEGIN

11

12 SELECT @uid := id FROM `user`13 WHERE identity_no=identityNo AND NAME=uName;14

15 IF @uid IS NULL THEN

16 select identityNo,uName,0ret;17 LEAVE label_at_start;18 END IF;19 update account set balance=balance+cAmount where uid=@uid;20 select identityNo,uName,1ret;21 ENDlabel_at_start$$22 DELIMITER ;

首先就是what the fuck的执行失败问题,调用该存储过程结果居然都是返回ret=0失败!!

2dae98c9bd33bac5db97d93c31b46f7b.png

还好当初写存储过程时,考虑到了结果查询,比较容易就发现为什么返回的uName是乱码?碰上这种问题,直觉就是数据库编码有问题,一查果然如此

e3bf66041542cbef60b0d7c121d7df4f.png

问题查出来了,怎么修正呢,正常剧情当然是改数据库默认编码为utf8了,但改编码后Mysql必须要重启,生产环境是你想重启就能重启的吗?好吧,还好mysql存储过程支持指定编码,修改存储过程,在uName部分指定编码集是utf8(补充虽然数据库默认字符集是latin1,但实际里面的表在创建时都默认指定utf8了,所以导致一直没发现生产环境居然有默认编码集问题)

IN uName VARCHAR(15) character set utf8,

改完后执行批量调用存储过程的sql,大致如下

call usp_SadEvent('123131231313123132','张三',3000);#数据库有

call usp_SadEvent('123454566778899999','李四',5000);#数据库无李四,或数据库里叫李斯

这里特别标明第一条数据库里是有对应数据,第二条在数据库中是查不到用户数据的,执行结果居然发现第二条“李四”的金额,被加到了“张三”身上,这又是什么鬼!

其实问题就出在mysql的默认声明参数上,只要在上面的调用语句下面再加一句

call usp_SadEvent('123131231313123132','张三',3000);#数据库有

call usp_SadEvent('123454566778899999','李四',5000);#数据库无select @uid;

执行结果很明显的就告诉你@uid是有值的,而且值为张三的uid,好吧,没想到Mysql中

SELECT @uid := id

这种隐式声明参数的方式居然会在整个对话期间内都有效,所以还是老老实实改成如下显式声明才能测试正确

declare u_id long;select `id` into u_id FROM `user`

最终完整修改后的存储过程应该如下

DROP PROCEDURE IF EXISTS`usp_SadEvent`;

DELIMITER $$CREATE PROCEDURE`usp_SadEvent`

(IN identityNo VARCHAR(20),IN uName VARCHAR(15) character setutf8,IN cAmount LONG)

label_at_start:BEGIN

declare u_id long;select `id` into u_id FROM `user`WHERE identity_no=identityNo AND NAME=uName;IF u_id IS NULL THEN

select identityNo,uName,0ret;

LEAVE label_at_start;END IF;update account set balance=balance+cAmount where uid=u_id;select identityNo,uName,1ret;ENDlabel_at_start$$

DELIMITER ;

哎,事后描述问题似乎很简单,实际排查这些问题还是挺坑的,哎……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值