前几天负责的理财产品线上出现问题:一客户赎回失败,查询交易记录时显示某条交易记录为其余人的卡号。程序员
交易的链路以下: 数据库
出现该问题后,咱们对日志进行了分析,发现主站收到的两笔流水号彻底相同,然而主站却没有作重复校验,将两笔订单(A和B)都发往基金系统,基金系统作了重复校验,收到A以后开始处理,收到B以后直接报错返回,A处理完后又正常返回。可是主站根据流水号更新数据库状态,却将两笔订单更新错了,致使客户的交易记录出错。并发
该问题虽然不会形成用户的资金损失或记帐出错,可是交易记录出错会带来极差的用户体验,引起客户投诉,并对公司声誉带来不良影响。所以主站经过增长重复校验来解决此问题。分布式
可是问题的根源在于为什么会产生重复的流水号,只有从源头上消灭重复的流水号,该问题才算完全解决,所以咱们对代码进行了分析。高并发
流水号由APP -server产生,并传入后续的交易。流水号生成代码以下: 性能
能够看出,流水号由13位时间戳+3位随机数+固定数字“38”组成。通常状况下,该规则生成的流水号是不会重复的,由于时间戳是精确到毫秒的。可是在高并发的状况下,同一毫秒收到多个请求,此时只能由三位随机数来保证流水号的惟一性。3d
虽然就单次请求来讲,与同一毫秒内其它请求的流水号重复的概率极小,能够忽略。假设每一毫秒有2个请求,那么这两个请求的3位随机数重复的几率为1/1000,不重复的几率为999/1000(假设是这么大的几率,没有通过数学计算)。咱们经过程序来看下流水号的重复几率:日志
程序运行结果以下(为了方便查看,随机数加了-用来分隔): cdn
程序运行屡次,也没法复现流水号重复的问题。但没法复现不表明没有问题,只能说明发生几率较小,所以须要调大循环次数。server
循环次数调大后,log输出已没法靠肉眼去看是否重复,须要将每一个流水号出现的次数存入Map,最后再看有多少个次数大于1的流水号。代码片断以下:
如何避免发生重复呢?目前我想到的有如下几种方法:
若是各位有好的想法,欢迎关注个人公众号(程序员顺仔)留言讨论~