原文地址:https://www.douyacun.com/article/8bec13a79bd42adda19f20120caf26fe
今天是2020年1月16号,记录这周发生的一件充值事故,1月12号周一下班后被通知某家代理商出现消耗金额超过充值金额800多万,当时第一反应是计费又出问题了,要帮忙定位问题,在家连zoom,翻看整个代理商从开户到现在每个月的充值数据,然后查询代理商每个月月月初的账户数据快照(实际每天晚上23:59都会快照一份存在hive中),一直到了晚上12点半,一直没有定位到问题出现的原因,然后领导就下线睡觉了,这个问题也出现一段时间不急于这一天了。
1月17号,大家都在吐槽年底了,为啥比平时还忙。针对昨天的bug,领导去支持了,我这边手上有两个大的功能点需要迭代上线。晚上回家后,被通知问题出现平台充值这边,不是计费的问题,头大,通过充值数据和消耗数据对比发现11月之前的正常的,12月开始出现消费大于充值金额;问题出现在11月份,还是完全没有思路,因为还是没有证据确定是充值问题还是计费问题,后续还是在翻看一直到12点半。
1月18号,下午定了一个会议室,专门来查这件事情,从代理商充值开始到12月1号00:00充值金额 - 12月1号00:00代理商下所有广告主的消耗金额 - 12月1号00:00代理商剩余金额 - 12月1号00:00代理商下广告主剩余金额。发现差值有:8486863.50元,这个时候可以错略确定的是充值问题,计费不会出现这么大的问题,而且19年1月份计费没有出现过重大bug。翻看代理商的充值记录有一条:8525163.53, 数值相近。不会是巧合,而且充值一般都是整数,问题出现了。
记得当时代理商是要预充值3000万,而数据库充值记录的金额字段是用的int(11), 单位:分,最多能存2147483647,所以当时后来又给代理商补了8525163.53,按道理这会应该it
也没有问题,问题出现账户表上,充值记录金额是有符号的,而账户余额是unsigned无符号的, 最大值到4294967295,是能存下3000万的。虽然充值记录显示2147483647,但实际账户确实是充值了3000万。
充值是有事务的,为啥没有回滚?如何避免这种情况的发生
MySQL默认情况下,当一个数据超出定义列的数据类型的范围时,数据会以数据类型所充许的最大值存储。不会出现报错,事务没有回滚。
在这种情况下,我们希望在插入或更新字段时,如果插入或更新的值超出定义的字段数据类型的范围时,提出报错,禁示修改数据,通过修改全局变量sq_mode STRICT_ALL_TABLES
实现
[SQL] insert into t6(`num`) value(2147483648);
[Err] 1264 - Out of range value for column 'num' at row 1
充值没有监控吗?为啥会出现这么重大bug
功能版本迭代,开始建表是int,后续有种改不动的感觉。大家都开始回避这个棘手问题。直至出现这样的问题,所以监控是非常必要的,代理商自助充值的功能也是在出现问题增加的监控,这是第二次出现充值问题。
总结, 经历两次充值事故后了以下几点:
big int
, decimal
,其他类型就不要考虑了,留坑而已STRICT_ALL_TABLES
最后这两点不管是用在哪里都合适,就是因为用在哪里都合适所以很难重视起来,只有吃过亏的才知道。