關於生成訂單號的解決方案

2021-06-12 23:16:12 字數 2866 閱讀 4690

關於生成訂單號的解決方案

電子商務及類電子商務的系統越來越多,我相信訂單號問題是這類系統中最常見不過的乙個問題了,但今天還是想談談。

這幾天由於工作需要接手了另外一同事前期開發的乙個交易系統,原本使用的是uniqid()函式生成的。uniqid()是根據系統時間經過一定演算法得到的乙個結果,關於uniqid()的詳情手冊上很清楚。

當時的生產方式是:

$order_sn = str_replace('.', '', uniqid('', true)); 這種方式理論上說會重複,但是在實際應用中我相信這種重複可以認為是不可能事件。但是,如果這件事情到此就結束的話我也就不會再寫這篇文章。這幾天做支付接入,國內某大型網路支付機構只支援傳遞最多16位的訂單號,無奈我只得調整訂單號的生產規則。

其實關於生成訂單號的方式非常多,大致有以下幾個引數被用到:1、自增字段,2、系統時間,3、隨機數,4、流水號。

一、資料庫自增字段

二、簡單的使用系統時間

三、系統時間加隨機數

四、系統時間加流水號

先說說資料庫自增字段,這種方式是最簡單有效的方式,但同時也存在很大的弊端:

1、以mysql為例的int型別最多儲存10位的數字,如果使用bigint則在使用php的mysql_insert_id()取上次插入id時會出現錯誤,當然這個錯誤是可以採用某些方法避免的。

2、很多時候業務邏輯需要在資料未插入系統之前就獲得訂單號以進行一系列的處理,這樣就容易出錯。比如當併發較高的時候系統獲取到下一次插入的id應該是10000,可是當真正insert的時候發現10000已經被其他插入行使用。

3、很容易透露出系統的銷量,從商業層面說這種方式不太合適。

4、表現不夠直觀,不能通過訂單號簡表達訂單資訊

簡單的使用系統時間也可以有多種方式比如直接使用time()生成10位數字,這種方式基本避免了資料庫自增字段的大部分弊端,但同時也產生的一些新的問題,比如:併發量稍高(峰值每秒一次以上,相信這是個很小的值)就會產生相同訂單號,而這是業務邏輯所不允許的。為了解決重複訂單問題而使用隨機數或者流水號。

先說隨機數,這東西就跟看上去的字面意思一樣,總顯得不那麼可靠,我認為盡量不使用它參與唯一標識。

再說流水號,既然叫流水號,它的性質其實和自增欄位一樣,不同的是或許每天或者每月流水號又會重新計數。總得有個地方來儲存下乙個(或者當前使用過的最大)流水號的值,如果存在檔案中那就需要考慮這個檔案的讀寫鎖的問題,就這個問題估計足夠寫書了,在此不予討論。如果以自增方式存在db中,那麼我們在程式生成訂單號之前需要多訪問(至少)一次db,這也就降低了程式效能,要知道資料庫訪問對程式的效能影響是非常明顯的

上面是一大堆廢話,說說我的解決思路(php),當然同時別忘了大前提是:限制長度16位

第一步:

$order_sn = date('ymdhis').substr(microtime(),2,4); 其實這種方式基本已經滿足需求了,無需訪問db無隨機數參與。但是如果兩次請求在相同的十萬分之一秒內產生,那麼相同訂單就產生了,看能否有辦法繼續提高。

date(『his』)所表達的結果其實就是000000到235959,而且其中很多數字不會被用到比如126998。一天86400秒,如果從一天的0:0:0算起直到23:59:59使用00000-86400就可以完全表示,這樣下來我們就完全可以把date(『his』)換成五位數字。既然time()函式就是按秒計數,那咱就取time()結果的後五位,同一天之內後五位不會重複出現,比如今天0:0:0後五位是98765,那麼到今天23:59:59後五位就應該是98765+86400去掉最高位,相信這個應該是很好理解的。

這就產生了第二步的結果:

$order_sn = date('ymd').substr(time(),-5).substr(microtime(),2,5); 這樣一來也導致無法直觀的表達出訂單生成的時分秒,但我認為(或者說從業務角度理解)這個屬於可接受範圍。同時這樣處理出現重複訂單的概率就降低到了第一步的1/10,我以為這應該不算乙個小數字。還不滿意?ok,那繼續!

想要繼續降低重複可能性那就繼續提高時間精度,但是我們的長度限制只有16位,看來只有減少部分不長變動的字元。

date(『ymd』)產生6位字元,而前兩位在一年之內都不會變化,第三到第四位也就是01-12。

前兩位我們可以使用a-z的字母來表示,系統開始執行的那一年用a,第二年用b,第三年用c……類推,我相信我寫的這程式執行不了10年。第三第四位完全可以使用一位16進製制數表示,這樣咱就又節約了兩位字元,這就可以在末尾加上00-99的隨機數。

現在看第三步的結果

$year_code = array('a','b','c','d','e','f','g','h','i','j'); $order_sn = $year_code[intval(date('y'))-2010]. strtoupper(dechex(date('m'))).date('d'). substr(time(),-5).substr(microtime(),2,5).sprintf('%02d',rand(0,99)); 理論上說出現重複訂單號的概率又降到了第二步的1/100。

做個簡單測試,寫個php檔案,連續10次echo出這三步結果得到的$order_sn,中間無任何多餘程式。

第一種方案基本得到10個相同的結果。

第二種方案基本得到10個不同的結果,主要是後兩位不同,一般末位差一。

第三種方案得到10個不同結果末四位不同

當然這個測試不具備多少說服力

優點:1、不用運算元據庫,效能較高。

2、較為直觀,不難看出訂單產生的大致時間

3、訂單號重複的概率極小,只有程式在百萬分之一秒內同時處理乙個以上的生成訂單號請求,而且同時生成的0-99的隨機數也一樣才會出現重複的訂單號。

其實就乙個非常簡單的問題,為什麼我要寫這麼多呢?或許是按耐不住找到解決方案的高興吧,也或許現在比較閒,也或許很久沒寫了手癢癢,希望有緣看到這篇文章的朋友多提意見。

關於訂單號的生成

現在在做乙個手機 的專案,boss叫我設計乙個訂單號,訂單號要求唯一。我冥思苦想,然後查閱了其他大 的生成方法,boss給我的要求是10位,好吧,這個重複性超級高。我是這樣設計的 使用者id後4位 日期 月日 隨機2位數 感覺這樣的重複性還是挺高的,假設有10w個使用者,那麼前面4位重複的就有10個...

訂單號生成

之前用uuid 因為太長改用16位因此在網上找到一下這種做法,年月日擷取 時間戳 在加隨機數 生成乙個訂單 獲取年份 var date j f c d e b h i a date gettime tostring var ordersn date new date getfullyear 2015...

PHP訂單號的生成

前陣子,公司有個電子商務專案,需要生成訂單號。當時的考慮很簡單,取系統時間加上隨機數,或者使用 uniqid 方法。我們都知道,訂單號最基本的要求就是唯一,這個條件必須滿足。仔細考慮下上述方法,在顧客購買量少的情況下,訂單重複的可能性為零,但是在購買高蜂期生成的訂單號重複是很有可能發生的。所以上述方...