銀聯16位數字訂單號
永遠不重複的生成算法
請尊重知識,請尊重原創 更多資料參考請見 http://www.cezuwang.com/listFilm?page=1&areaId=906&filmTypeId=1
一、 前提背景
相信作過銀聯支付的都知道,銀聯的訂單號要求商戶提供一個不重複的16位數字訂單號(不重複指的是對商戶自己,不用考慮銀聯有多個商戶會與其餘商戶的訂單號重複)。16位數其實很短,要考慮每秒併發1w或者10w或100萬時,重複訂單號將數不過來。
須要考慮的因素:
若使用數據庫保存流水號,集羣部署時,同步關鍵字再也不有效。固然同步對性能也有很是大的影響;
若使用時間,必需要精確到毫秒、微妙級別,長度就不止16位了。
若使用數據庫字段自增,數據庫併發時硬件將吃不消。
獲取訂單號時檢查表的最大值,這種方案是最不可取的。
如下將給出本人通過深刻研究的三種方案,按順序,最優的方案爲第三個。
備註:
若是要測試產生重複訂單號的狀況,能夠創建一個表,把訂單號字段設置爲惟一性,而後開啓1000或10000或更多的線程去請求方法,每一個線程循環5次或10次來請求,在方法裏面寫插入語句。或者可使用Apache的ab工具併發測試。
使用方法:ab -n5000 -c5000 http://192.168.1.102:8888/kjcx/aaa.action
二、 可選方案一
本方案使用的是當前時間,包括毫秒數、納秒數,不須要數據庫參與計算,性能不用說。
算法:java
Java代碼 web
OrderId=machineId+(System.currentTimeMillis()+"").substring(1)+(System.nanoTime()+"").substring(7,10);
講解:
參數machineId:是集羣時的機器代碼,能夠1-9任意。部署時,分別爲部署的項目手動修改該值,以確保集羣的多臺機器在系統時間上不一致的問題(毫無疑問每臺機器的毫秒數基本上不一致)。
參數System.currentTimeMillis():這是java裏面的獲取1970年到目前的毫秒數,是一個13位數的數字,與Date.getTime()函數的結果同樣,好比1378049585093。通過研究,在2013年,前三位是137,在2023年是168,到2033年才199.因此,我決定第一位數字1能夠去掉,不要佔位置了。能夠確定絕大多數系統用不了10年20年。這樣,參數2就變成了12位數的數字,加上參數1machineId才13位數。
參數System.nanoTime():這是java裏面的取納秒數,通過深刻研究,在同一毫秒內,位置7,8,9這三個數字是會變化的。因此決定截取這三個數字出來拼接成一個16位數的訂單號。
總結:理論上此方案在同一秒內,能夠應對1000*1000個訂單號,可是通過測試,在每秒併發2000的時候,仍是會出現2-10個重複。
三、 可選方案二
本方案使用的是得到會話ID(sessionId)來產生hashCode。
算法:算法
Java代碼 數據庫
OrderId=machineId+session().getId().hashCode();
講解:
參數machineId再也不講解,與方案一致。
參數2 session().getId().hashCode()是值在web系統中獲取用戶瀏覽器與web容器的惟一會話編號,再把該會話ID轉換爲該字符串的hashCode值,如1939354961。該值多是一個11位數的或10位數的,或者在前面還會出現-號,也就是有可能該值是負數,不要緊,取正。而後再對該值進行左補0到15位數,基本上能夠應對位數不一致的問題。
咱們知道,hashCode是jdk根據對象的地址或者字符串或者數字算出來的int類型的數值。能夠想象,hashCode的值若是出現重複,那就是一個值了,而不是不一樣的值。又由於sessionId是客戶端、與瀏覽器有關聯的,因此基本上不會出現重複,可是若是用戶在同一個會話有效期內、同一個版本的瀏覽器,生成2次就無效了,由於會話ID是一致的。
總結:該算法,能夠確保不重複的機率很小,可是須要本身特殊處理同會話同瀏覽器生成1次以上訂單號的問題,此算法沒有通過調試,略過,您請看方案三。
四、 可選方案三
本方案在基於方案二的基礎上作了修改,使用的使用UUID而不是會話id。
UUID是指在一臺機器上生成的數字,它保證對在同一時空中的全部機器都是惟一的,這個不重複性全世界人民都知道。固然,既然字符串值不重複,那對應的hashCode也是同樣,不會重複。
算法:瀏覽器
Java代碼 session
OrderId=machineId+UUID.randomUUID().toString().hashCode();
講解:
參數1再也不解釋。
參數2是值生成UUID而後取它的hashCode值,通過測試,徹底沒有一點問題。您能夠開1000w的併發去測試插入吧,只要數據庫不會報惟一性錯誤,那就沒問題。
總結:
hashCode這個算法從搞軟件開始到如今這麼多年,一直沒派上用場,此次大大的用上了。解決了問題。請同志們之後善用這個東西。
五、 附錄:方案三的算法代碼併發
Java代碼dom
public static String getOrderIdByUUId() { int machineId = 1;//最大支持1-9個集羣機器部署 int hashCodeV = UUID.randomUUID().toString().hashCode(); if(hashCodeV < 0) {//有多是負數 hashCodeV = - hashCodeV; } // 0 表明前面補充0 // 15 表明長度爲15 // d 表明參數爲正數型 return machineId+String.format("%015d", hashCodeV); }
方案三其實也就一個函數,很簡便。函數