生成訂單號

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

(2012-09-21 11:04:45)
標籤:

雜談

分類: php
關於生成訂單號的解決方案
電子商務及類電子商務的系統愈來愈多,我相信訂單號問題是這類系統中最多見不過的一個問題了,但今天仍是想談談。
這幾天因爲工做須要接手了另一同事前期開發的一個交易系統,本來使用的是uniqid()函數生成的。uniqid()是根據系統時間通過必定算法獲得的一個結果,關於uniqid()的詳情手冊上很清楚。
當時的生產方式是:
 
$order_sn = str_replace('.', '', uniqid('', true));
 
這種方式理論上說會重複,可是在實際應用中我相信這種重複能夠認爲是不可能事件。可是,若是這件事情到此就結束的話我也就不會再寫這篇文章。這幾天作支付接入,國內某大型網絡支付機構只支持傳遞最多16位的訂單號,無奈我只得調整訂單號的生產規則。
其實關於生成訂單號的方式很是多,大體有如下幾個參數被用到:一、自增字段,二、系統時間,三、隨機數,四、流水號。
 
1、數據庫自增字段
2、簡單的使用系統時間
3、系統時間加隨機數
4、系統時間加流水號
 
先說說數據庫自增字段,這種方式是最簡單有效的方式,但同時也存在很大的弊端:
一、以mysql爲例的int類型最多存儲10位的數字,若是使用bigint則在使用php的mysql_insert_id()取上次插入id時會出現錯誤,固然這個錯誤是能夠採用某些方法避免的。
 
二、不少時候業務邏輯須要在數據未插入系統以前就得到訂單號以進行一系列的處理,這樣就容易出錯。好比當併發較高的時候系統獲取到下一次插入的ID應該是10000,但是當真正insert的時候發現10000已經被其餘插入行使用。
 
三、很容易透露出系統的銷量,從商業層面說這種方式不太合適。
 
四、表現不夠直觀,不能經過訂單號簡表達訂單信息
 
簡單的使用系統時間也能夠有多種方式好比直接使用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('d',rand(0,99));
 
理論上說出現重複訂單號的機率又降到了第二步的1/100。
作個簡單測試,寫個php文件,連續10次echo出這三步結果獲得的$order_sn,中間無任何多餘程序。
第一種方案基本獲得10個相同的結果。
第二種方案基本獲得10個不一樣的結果,主要是後兩位不一樣,通常末位差一。
第三種方案獲得10個不一樣結果末四位不一樣
固然這個測試不具有多少說服力
 
優勢:
一、不用操做數據庫,性能較高。
二、較爲直觀,不難看出訂單產生的大體時間
三、訂單號重複的機率極小,只有程序在百萬分之一秒內同時處理一個以上的生成訂單號請求,並且同時生成的0-99的隨機數也同樣纔會出現重複的訂單號。
相關文章
相關標籤/搜索