1. 程式人生 > >高併發生成訂單號

高併發生成訂單號

銀聯16位數字訂單號
永遠不重複的生成演算法

 請尊重知識,請尊重原創 更多資料參考請見http://www.cezuwang.com/listFilm?page=1&areaId=906&filmTypeId=1
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


2、 可選方案一 
本方案使用的是當前時間,包括毫秒數、納秒數,不需要資料庫參與計算,效能不用說。
演算法:

Java程式碼  收藏程式碼
  1. OrderId=  
  2. machineId+  
  3. (System.currentTimeMillis()+"").substring(1)+  
  4. (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個重複。


3、 可選方案二
本方案使用的是獲得會話ID(sessionId)來產生hashCode。
演算法:

Java程式碼  收藏程式碼
  1. OrderId=  
  2. machineId+  
  3. 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次以上訂單號的問題,此演算法沒有經過除錯,略過,您請看方案三。


4、 可選方案三
本方案在基於方案二的基礎上做了修改,使用的使用UUID而不是會話id。
UUID是指在一臺機器上生成的數字,它保證對在同一時空中的所有機器都是唯一的,這個不重複性全世界人民都知道。當然,既然字串值不重複,那對應的hashCode也是一樣,不會重複。
演算法:

Java程式碼  收藏程式碼
  1. OrderId=  
  2. machineId+  
  3. UUID.randomUUID().toString().hashCode();  



講解:
引數1不再解釋。
引數2是值生成UUID然後取它的hashCode值,經過測試,完全沒有一點問題。您可以開1000w的併發去測試插入吧,只要資料庫不會報唯一性錯誤,那就沒問題。
總結:
hashCode這個演算法從搞軟體開始到現在這麼多年,一直沒派上用場,這次大大的用上了。解決了問題。請同志們以後善用這個東西。
5、 附錄:方案三的演算法程式碼

Java程式碼  收藏程式碼
  1. 6、  public static String getOrderIdByUUId() {  
  2. 7、          int machineId = 1;//最大支援1-9個叢集機器部署  
  3. 8、          int hashCodeV = UUID.randomUUID().toString().hashCode();  
  4. 9、          if(hashCodeV < 0) {//有可能是負數  
  5. 10、             hashCodeV = - hashCodeV;  
  6. 11、         }  
  7. 12、         // 0 代表前面補充0       
  8. 13、         // 4 代表長度為4       
  9. 14、         // d 代表引數為正數型  
  10. 15、         return machineId+String.format("%015d", hashCodeV);  
  11. 16、     }  



方案三其實也就一個函式,很簡便。

相關推薦

併發生成訂單

銀聯16位數字訂單號 永遠不重複的生成演算法  請尊重知識,請尊重原創 更多資料參考請見http://www.cezuwang.com/listFilm?page=1&areaId=906&filmTypeId=11、 前提背景相信做過銀聯支付的都知道,銀

mysql生成訂單

DROP TABLE IF EXISTS `order_seq`;CREATE TABLE `order_seq` (`timestr` int(11) NOT NULL,`order_sn` int(11) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf

redis 生成訂單學習

題目是生成明天的訂單號,刪除昨天的訂單號,在redis操作,訂單號暫定規則為年月日接五位數。如2018102200001 當一個訂單生成的時候, 去redis去取訂單號,去玩後刪掉。這個取和刪除操作是一個命令發過去的。不能寫成兩個語句。 參考部落格: redis五種結

PHP 生成訂單

$order_id_main = date('YmdHis') . rand(10000000,99999999); //訂單號碼主體長度 $order_id_len = strlen($order_

java生成訂單的一種思路(生成友好的訂單)

比如訂單號碼要求20位字串,除了8位的yyyymmdd外,還有12位可以自己去做。 提供一個工具類,根據一個數字生成訂單號 result str = yyyymmdd + 這個int的 格式化結果,格式化就是補0,比如今天是16年12.30第一個訂單號是1那麼完整訂單號為2

php生成訂單的解決方案

在php中關於生成訂單號的解決方案,值得借鑑。 前期開發的一個交易系統,原本使用的是uniqid()函式生成的。 uniqid()是根據系統時間經過一定演算法得到的一個結果,關於uniqid()的詳情手冊上很清楚。 當時的生產方式是: $order_sn = str_rep

怎麼生成訂單

package com.zhongjiu.utils; import java.text.SimpleDateFormat; import java.util.Random; /**  * 生成訂單的工具類   */ public class DingDanUtil {pu

PHP生成訂單方法

第一種PHP生成唯一單號的方法 PHP程式碼 $str = date('Ymd') . str_pad(mt_rand(1,

java web系統在併發下如何實現訂單生成唯一?

java web系統在高併發下如何實現訂單號生成唯一? 系統訂單號規則:XXXX(固定字元)+年(後兩位)月日+流水號。流水號每天重新從1開始。 系統訂單號產生唯一的方案有哪幾種?由於訂單號規則已經確定,無法使用時間戳及隨機數,有哪些方案可以使用? 注:資料庫mysql,訂單號不是訂單表的主鍵

併發生成自定義規則的訂單

目錄 背景 規則 問題 分析 思路 資料庫 執行緒鎖 方案 討論 背景 半年以前做的一個流程相關的專案,近期在做效能測試;之前的功能測試已經做完了,都沒有什麼問題。    專案採用的springmvc框架,生成訂單號以及儲存訂單號都是在activ

併發下怎樣生成唯一的訂單

方案一: 如果沒有併發,訂單號只在一個執行緒內產生,那麼由於程式是順序執行的,不同訂單的生成時間戳正常不同,因此用時間戳+隨機數(或自增數)就可以區分各個訂單。 如果存在併發,且訂單號是由一個程序中的多個執行緒產生的,那麼只要把執行緒ID新增到序列號中就可以保證訂單號唯一。

javaEE併發之如何產生唯一不重複訂單

javaEE高併發之如何產生唯一不重複訂單號 1.方案一:使用程序ID,執行緒ID,IP,MAC地址和時間戳進行拼接產生訂單號 (1)如果沒有併發,訂單號只在一個執行緒內產生,那麼由於程式是順序執行的,不同訂單的生成時間戳正常不同,因此用時間戳+隨機數(或自增數)就可以區分各個訂單。 (

併發下唯一訂單生成器【16位數字訂單

高併發下唯一訂單號生成思考? 訂單號3個性質:1.唯一性 2.不可推測性 3.效率性可選方案一 本方案使用的是當前時間,包括毫秒數、納秒數,不需要資料庫參與計算,效能不用說。 public static String genId(String machineId){

併發環境下生成訂單唯一流水號方法:SnowFlake

業務需求: 訂單號不能重複 訂單號沒有規則,即編碼規則不能加入任何和公司運營相關的數 據,外部人員無法通過訂單ID猜測到訂單量。不能被遍歷。 訂單號長度固定,且不能太長 易讀,易溝通,不要出現數字字母換亂現象 生成耗時 關於訂單號的生成,一些比較簡單的方案

訂單生成函數

return 支付 pre 自增 date use func 增長 訂單 以下是我在做電商系統用的訂單號生成函數 ::/** * 訂單序列生成 16位 * $type支付/提取類型 * $usertype用戶類型 * $oid 訂單自增長 */public functi

PHP生成唯一訂單

sub 擔心 ech 生成 int sprintf and return func function create_order_no() { $order_no = date('Ymd').substr(implode(NULL, array_map

生成一個不重復的訂單(php)

AS stat imp 不相信 ID ascii碼 ascii 刪除 UNC /** * 生成訂單號 * * 用uniqid獲取一個基於當前的微秒數生成的唯一不重復的字符串(但是他的前7位貌似很久才會發生變動,所以不用考慮可刪除),取其第8到13位。但是這個字符串裏面有英文

併發下的訂單與庫存的處理

問題:一件商品只有100個庫存,現在有1000或者更多的使用者來購買,每個使用者計劃同時購買1個到幾個不等商品。如何保證庫存在高併發的場景下是安全的。 1.不多發 2.不少發 下單涉及的一些步驟 1.下單 2.下單同時預佔庫存 3.支付 4.支付成功真正減扣庫存 5.取消訂

PHP生成一個唯一訂單,年月日這種高大尚的例子

/生成一個訂單號 function getOrderNum(){     $order_number = date('Ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7,

併發搶紅包案列以及使用鎖,版本,redis快取解決,專案可執行,詳細註釋(三)

1redis搶紅包實現 在redis中首先設定紅包的數量和金額,使用者搶到紅包之後,在redis中計算紅包數量-1,儲存使用者的資訊,直到紅包被搶完。再將使用者資訊批量儲存到資料庫中。由於redis的計算是原子性的,所以不會出現資料錯誤,可以理解成atomic系列 具體的環境搭建請檢視