• 原本预期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,0 ret;
    17   LEAVE label_at_start;
    18 END IF;
    19    update account set balance=balance+cAmount where uid=@uid;
    20     select identityNo,uName,1 ret;
    21 END label_at_start$$
    22 DELIMITER ;

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

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

    问题查出来了,怎么修正呢,正常剧情当然是改数据库默认编码为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 set utf8,
    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,0 ret;
      LEAVE label_at_start;
    END IF;
       update account set balance=balance+cAmount where uid=u_id;
        select identityNo,uName,1 ret;
    END label_at_start$$
    DELIMITER ;

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

  • 相关阅读:
    【UOJ #268】【清华集训2016】数据交互(动态DP)
    【UOJ #267】【清华集训2016】魔法小程序(前缀和)
    【UOJ #266】【清华集训2016】Alice和Bob又在玩游戏(SG函数+01Trie)
    【CSP-S 2019题解】
    【CSP 2019游记】
    【CSP-S 2019模拟题解】
    sql语句: update和sql函数的冲突
    http协议之实践巩固(深度篇一)
    不错的开发工具做下记录
    javascrpt之this指向问题
  • 原文地址:https://www.cnblogs.com/starfd/p/6907663.html
Copyright © 2020-2023  润新知