php訂單生成唯一Id


一般用到一個函數:

uniqid(prefix,more_entropy)
參數 描述
prefix 可選。為 ID 規定前綴。如果兩個腳本恰好在相同的微秒生成 ID,該參數很有用。
more_entropy 可選。規定位於返回值末尾的更多的熵。

說明

如果 prefix 參數為空,則返回的字符串有 13 個字符串長。如果 more_entropy 參數設置為 true,則是 23 個字符串長。

如果 more_entropy 參數設置為 true,則在返回值的末尾添加額外的熵(使用組合線形同余數生成程序),這樣可以結果的唯一性更好。

由於基於系統時間,通過該函數生成的 ID 不是最佳的。如需生成絕對唯一的 ID,請使用 md5() 函數(請在字符串函數參考中查找)

最簡單的生成

md5(uniqid(rand(), true)); rand()可以換成'',空

一篇文章:

關於生成訂單號的解決方案
電子商務及類電子商務的系統越來越多,我相信訂單號問題是這類系統中最常見不過的一個問題了,但今天還是想談談。
這幾天由於工作需要接手了另外一同事前期開發的一個交易系統,原本使用的是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的隨機數也一樣才會出現重復的訂單號。

轉自:http://www.redtamo.com/default/create_order_sn.html


更多:
http://segmentfault.com/q/1010000000095834
http://blog.sina.com.cn/s/blog_4c925dca0100ay4b.html






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM