编程语言一次订单号重复引起的事故

    作者:佚名更新于: 2020-11-11 15:06:47

    大神带你学编程,欢迎选课

    一次订单号重复引起的事故,把我坑惨了!在过去的几十年间,大量的编程语言被发明、被取代、被修改或组合在一起。尽管人们多次试图创造一种通用的程序设计语言,却没有一次尝试是成功的。之所以有那么多种不同的编程语言存在的原因是,编写程序的初衷其实也各不相同;新手与老手之间技术的差距非常大,而且有许多语言对新手来说太难学;还有,不同程序之间的运行成本(runtime cost)各不相同。

    系统出现了两个一模一样的订单号,订单的内容却不是不一样的,而且系统在按照订单号查询的时候一直抛错,也没法正常回调,而且事情发生的不止一次,所以这次系统升级一定要解决掉。

    编程语言一次订单号重复引起的事故_编程语言_java_  开发工具_课课家

    我们线上出了一次事故,这个事故的表象是这样的:

    系统出现了两个一模一样的订单号,订单的内容却不是不一样的,而且系统在按照订单号查询的时候一直抛错,也没法正常回调,而且事情发生的不止一次,所以这次系统升级一定要解决掉。

    经手的同事之前也改过几次,不过效果始终不好,总会出现订单号重复的问题,所以趁着这次问题我好好的理了一下我同事写的代码。

    这里简要展示下当时的代码:

    1. /**  
    2.  * OD单号生成  
    3.  * 订单号生成规则:OD + yyMMddHHmmssSSS + 5位数(商户ID3位+随机数2位) 22位  
    4.  */  
    5. public static String getYYMMDDHHNumber(String merchId){  
    6.       StringBuffer orderNo = new StringBuffer(new SimpleDateFormat("yyMMddHHmmssSSS").format(new Date()));  
    7.       if(StringUtils.isNotBlank(merchId)){  
    8.           if(merchId.length()>3){  
    9.               orderNo.append(merchId.substring(0,3));  
    10.           }else {  
    11.               orderNo.append(merchId);  
    12.           }  
    13.       }  
    14.       int orderLength = orderNo.toString().length();  
    15.       String randomNum = getRandomByLength(20-orderLength);  
    16.       orderNo.append(randomNum);  
    17.       return orderNo.toString();  
    18. }  
    19.   /** 生成指定位数的随机数 **/  
    20.   public static String getRandomByLength(int size){  
    21.       if(size>8 || size<1){  
    22.           return "";  
    23.       }  
    24.       Random ne = new Random();  
    25.       StringBuffer endNumStr = new StringBuffer("1");  
    26.       StringBuffer staNumStr = new StringBuffer("9");  
    27.       for(int i=1;i<size;i++){  
    28.           endNumStr.append("0");  
    29.           staNumStr.append("0");  
    30.       }  
    31.       int randomNum = ne.nextInt(Integer.valueOf(staNumStr.toString()))+Integer.valueOf(endNumStr.toString());  
    32.       return String.valueOf(randomNum);  
    33.   }       

    可以看到,这段代码写的其实不怎么好,代码部分暂且不议,代码中使订单号不重复的主要因素点是随机数和毫秒,可是这里的随机数只有两位,在高并发环境下极容易出现重复问题。

    同时毫秒这一选择也不是很好,在多核CPU多线程下,一定时间内(极小的)这个毫秒可以说是固定不变的(测试验证过),所以这里我先以100个并发测试下这个订单号生成。

    测试代码如下:

    1. public static void main(String[] args) {  
    2.     final String merchId = "12334";  
    3.     List<String> orderNos = Collections.synchronizedList(new ArrayList<String>());  
    4.     IntStream.range(0,100).parallel().forEach(i->{  
    5.         orderNos.add(getYYMMDDHHNumber(merchId));  
    6.     });  
    7.     List<String> filterOrderNos = orderNos.stream().distinct().collect(Collectors.toList());  
    8.     System.out.println("生成订单数:"+orderNos.size());  
    9.     System.out.println("过滤重复后订单数:"+filterOrderNos.size());  
    10.     System.out.println("重复订单数:"+(orderNos.size()-filterOrderNos.size()));  

    果然,测试的结果如下:

    1. 生成订单数:100  
    2. 过滤重复后订单数:87  
    3. 重复订单数:13 

    当时我就震惊

课课家教育

未登录