记一次订单号事故
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
当时我就震惊
以上是关于记一次订单号事故的主要内容,如果未能解决你的问题,请参考以下文章