1. 程式人生 > 實用技巧 >[轉]一次訂單號重複的事故

[轉]一次訂單號重複的事故

一次訂單號重複的事故差點被開除

funnyZpC碼匠筆記今天

作者:funnyZpC

cnblogs.com/funnyzpc/p/13541713.html去年年底的時候,我們線上出了一次事故,這個事故的表象是這樣的:
系統出現了兩個一模一樣的訂單號,訂單的內容卻不是不一樣的,而且系統在按照 訂單號查詢的時候一直拋錯,也沒法正常回調,而且事情發生的不止一次,所以 這次系統升級一定要解決掉。
經手的同事之前也改過幾次,不過效果始終不好:總會出現訂單號重複的問題, 所以趁著這次問題我好好的理了一下我同事寫的程式碼。這裡簡要展示下當時的程式碼:
/**
*OD單號生成
*訂單號生成規則:OD + yyMMddHHmmssSSS + 5位數(商戶ID3位+隨機數2位) 22位
*/
publicstaticStringgetYYMMDDHHNumber(StringmerchId){
StringBufferorderNo=newStringBuffer(newSimpleDateFormat("yyMMddHHmmssSSS").format(newDate()));
if(StringUtils.isNotBlank(merchId)){
if(merchId.length()>3){
orderNo.append(merchId.substring(0,3));
}else{
orderNo.append(merchId);
}
}
intorderLength=orderNo.toString().length();
StringrandomNum=getRandomByLength(20-orderLength);
orderNo.append(randomNum);
returnorderNo.toString();
}


/**生成指定位數的隨機數**/
publicstaticStringgetRandomByLength(intsize){
if(size>8||size<1){
return"";
}
Randomne=newRandom();
StringBufferendNumStr=newStringBuffer("1");
StringBufferstaNumStr=newStringBuffer("9");
for(inti=1;i<size;i++){
endNumStr.append("0");
staNumStr.append("0");
}
intrandomNum=ne.nextInt(Integer.valueOf(staNumStr.toString()))+Integer.valueOf(endNumStr.toString());
returnString.valueOf(randomNum);
}
可以看到,這段程式碼寫的其實不怎麼好,程式碼部分暫且不議,程式碼中使訂單號不重複的主要因素點是隨機數和毫秒,可是這裡的隨機數只有兩位在高併發環境下極容易出現重複問題,同時毫秒這一選擇也不是很好,在多核CPU多執行緒下,一定時間內(極小的)這個毫秒可以說是固定不變的(測試驗證過),所以這裡我先以100個併發測試下這個訂單號生成,關注微信訂閱號碼匠筆記,回覆架構獲取一些列的架構知識。測試程式碼如下:
publicstaticvoidmain(String[]args){
finalStringmerchId="12334";
List<String>orderNos=Collections.synchronizedList(newArrayList<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
當時我就震驚