记一次订单号事故

Posted edda_huang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记一次订单号事故相关的知识,希望对你有一定的参考价值。

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

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

/**
       * OD单号生成
       * 订单号生成规则:OD + yyMMddHHmmssSSS + 5位数(商户ID3位+随机数2位) 22位
       */
      public static String getYYMMDDHHNumber(String merchId){
          StringBuffer orderNo = new StringBuffer(new SimpleDateFormat("yyMMddHHmmssSSS").format(new Date()));
          if(StringUtils.isNotBlank(merchId)){
              if(merchId.length()>3){
                  orderNo.append(merchId.substring(0,3));
              }else {
                  orderNo.append(merchId);
              }
          }
          int orderLength = orderNo.toString().length();
          String randomNum = getRandomByLength(20-orderLength);
          orderNo.append(randomNum);
          return orderNo.toString();
      }
  
  
      /** 生成指定位数的随机数 **/
      public static String getRandomByLength(int size){
          if(size>8 || size<1){
              return "";
          }
          Random ne = new Random();
          StringBuffer endNumStr = new StringBuffer("1");
          StringBuffer staNumStr = new StringBuffer("9");
          for(int i=1;i<size;i++){
              endNumStr.append("0");
              staNumStr.append("0");
          }
          int randomNum = ne.nextInt(Integer.valueOf(staNumStr.toString()))+Integer.valueOf(endNumStr.toString());
          return String.valueOf(randomNum);
      }

可以看到,这段代码写的其实不怎么好,代码部分暂且不议,代码中使订单号不重复的主要因素点是随机数和毫秒,可是这里的随机数只有两位
在高并发环境下极容易出现重复问题,同时毫秒这一选择也不是很好,在多核CPU多线程下,一定时间内(极小的)这个毫秒可以说是固定不变的(测试验证过),所
以这里我先以100个并发测试下这个订单号生成,测试代码如下:

public static void main(String[] args) {
        final String merchId = "12334";
        List<String> orderNos = Collections.synchronizedList(new ArrayList<String>());
        IntStream.range(0,100).parallel().forEach(i->{
            orderNos.add(getYYMMDDHHNumber(merchId));
        });

        List<String> filterOrderNos = orderNos.stream().distinct().collect(Collectors.toList());

        System.out.println("生成订单数:"+orderNos.size());
        System.out.println("过滤重复后订单数:"+filterOrderNos.size());
        System.out.println("重复订单数:"+(orderNos.size()-filterOrderNos.size()));
    }

果然,测试的结果如下:

生成订单数:100
过滤重复后订单数:87
重复订单数:13

当时我就震惊

以上是关于记一次订单号事故的主要内容,如果未能解决你的问题,请参考以下文章

记一次由Redis分布式锁造成的重大事故,避免以后踩坑!

Mongodb---记一次事故故障

记一次Spring配置事故

记一次ES 事故

记一次ES 事故

记一次最近生产环境项目中发生的两个事故及处理方法