1. 基於時間+隨機碼生成id
php提供了一個生成唯一值生成函式uniqid($prefix,$more_entropy),這是一個基於毫秒級時間生成id的函式,不帶引數執行輸出13位字元隨機碼,$prefix返回隨機碼的字首,$more_entropy設為true時為加熵,返回字元會變為23位(不包括$prefix)。編輯程式碼如下:
- 單程式版程式碼
$start_time = microtime(true); $container = []; $all_count = 1000000; //生成id總數量(一百萬個) for ($i = 0; $i < $all_count; $i++) { //生成隨機id $random = uniqid(); //長這樣子:5e1401c70b7db $container[$random] = $i; //去掉重複id } $waste_time = bcsub(microtime(true), $start_time, 4);//計算時間 $conclusion = '耗時:' . $waste_time . 's,共生成id個數:' . $all_count . ',重複id個數:' . ($all_count - count($container)); dump($conclusion);
執行程式碼,返回
多次重複執行,發現重複id都是0,耗時都在60秒以上,平均每秒可以生成一兩萬個id,感覺uniqid()不加引數也不錯嘛,但是這是幻覺而已,壞得很,經不起考驗的。
- 多程式版程式碼
$start_time = microtime(true);
$process_count = 50; //程式數量
$all_count = 1000000; //生成id總數量(一百萬個)
$per_process_count = floor($all_count / $process_count); //每個程式生成id總數量
for ($i = 0; $i < $process_count; $i++) {
//開多程式模擬真實場景
MultiProcessHelper::instance($process_count)->multiProcessTask(function () use ($per_process_count, $i) {
//生成隨機id
$container = [];
for ($j = 0; $j < $per_process_count; $j++) {
$random = uniqid(); //長這樣子:5e1401c70b7db
$container[] = $random;
}
//把生成的id放到快取裡面
Cache::set($i, $container);
});
}
MultiProcessHelper::recycleProcess();//回收子程式
//以下是整理多個併發程式生成id的重複情況
$container = [];
for ($i = 0; $i < $process_count; $i++) {
$res = Cache::get($i);
foreach ($res as $v) {
$container[$v] = 0; //去掉重複id
}
}
$waste_time = bcsub(microtime(true), $start_time, 4);//計算時間
$conclusion = '耗時:' . $waste_time . 's,共生成id個數:' . $all_count . ',重複id個數:' . ($all_count - count($container));
dump($conclusion);
執行程式碼,返回
結果發現時間變短了,平均每秒生成的id數量達到了10萬個。但是別忘了,非同步執行任務的程式是50個,榨乾了cpu了。再看,重複個數不再為0,而是有10萬個之多,大概1/10都重複了,所以不加熵使用uniqid不能保證生成唯一id。
如果設定$prefix為mt_rand(),mt_rand()會生成0到最大隨機數數(21億左右)的隨機整數,$more_entropy=true,那樣將返回更加隨機的id,利用這一點設計隨機碼生成規則。編輯程式碼如下:
$random = uniqid(mt_rand(), true); //長這樣子:21083019475e14021cdceb11.02999582
- 單程式版執行結果
這個超強了,每秒可以生成百萬級別的id(加熵之後,使用的演算法不一樣了,總之加熵時uniqid更快了),重複率為0
- 多程式版執行結果
重複率為0,可以斷定一該方式生成唯一id可行,效能和重複概率表現很好,唯一不足就是id無序
2. 使用session_create_id()
該函式是php7.1之後提供的,是php用來生成session_id使用的,php使用它來生成每個請求會話,應該唯一是相當好的,不然呢!測試一波:
$random = session_create_id(); //長這樣子:6khfg75a13khre330nqu1t84ab
- 單程式版執行結果
- 多程式版執行結果
由上可見,session_create_id效能和重複率都表現不錯,值得信賴
3. 使用uuid
uuid已經形成的國際規範,目前出來的版本有四五個,最常用的是由時間戳+順序號+機器標識+程式標識規則生成的id,與此相關的composer包很多了,不造重車輪是最大的生產力,直接選用 https://packagist.org/packages/ramsey/uuid 包,測驗一下:
$random = Uuid::uuid1()->toString(); //長這樣子:480dac52-3102-11ea-89e3-525400cae48b
- 單程式版執行結果
- 多程式版執行結果
結果令人意外,uuid生成速度還行,但是多程式非同步生成時,並不能保證高標準的唯一性,出現了8個重複的,雖然很少,但是很意外,說好的全球唯一呢,有點虛,哈哈!總的來說還是不錯的,重複率已經做到相當低了。
4. 使用雪花演算法
以上三種都是隻會生成無序的隨機字元,但是我們有些時候需要依靠唯一id對資料庫進行排序的,比如我們要生成全庫唯一id時,我們就需要id滿足順序性、整數型別(提高索引效率),而由雪花演算法生成的id可以滿足這一點(雪花id有時間順序)。雪花演算法原理圖如下:
安裝composer包:https://packagist.org/packages/godruoyi/ph... 來使用
$snowflake=new Snowflake();
$random = $snowflake->id(); //長這樣子:55141599398592512
- 單程式版執行結果
- 多程式版執行結果
分析結果,單程式使用雪花演算法生成id,可以保持十幾萬個每秒的生成速度,且重複率保持為0,但是去到併發生成時,問題出現了,重複了很多,重複率1/2,恐怖,可斷,雪花演算法如上面那樣簡單使用的話,是難以適合高併發場景的!
5.id計算器生成id
id計算器生成id就是的首要工作就是要設定一個公共變數,該變數增量為1,每次程式從這裡申請一次id,id的值都會加一,這樣一直累加下去,就保證了全域性唯一性,且都是整數。這方面,Redis很能勝任,一是效能很好,二是跨機器,另外Redis提供的原子性函式incr,簡直天造地設了。來,操作一波:
$random = Cache::inc('id'); //長這樣子:1,2,3,4,5,...
- 單程式版執行結果
- 多程式版執行結果
這種方式生成的id,能夠保持很好的id遞增性,即保證的順序性,由於incr是原子操作,重複率幾乎為0,產生速率,有賴於Redis的軟體效能、網路等等因素,也算是了比較好方案了。
以上各種id生成方式,各有各的優缺點,不能一概而論。要哪一種更好,還是要看具體需求,適合就是最好的。但總的來說,唯一性、效能是絕對高尚的。
除了上述幾種方式之外,還有很多生成唯一id的方案,也可以奇技淫巧一下(猥瑣發育),在上面幾種方式為基礎打造新的生成方式
原創不易,分享快樂,渴望動力
本作品採用《CC 協議》,轉載必須註明作者和本文連結