探索 PHP 如何生成全域性唯一的 id

luler發表於2020-01-10

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);

執行程式碼,返回

探索 PHP 如何生成全域性唯一的 id

多次重複執行,發現重複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);

執行程式碼,返回

探索 PHP 如何生成全域性唯一的 id

結果發現時間變短了,平均每秒生成的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
  • 單程式版執行結果

探索 PHP 如何生成全域性唯一的 id

這個超強了,每秒可以生成百萬級別的id(加熵之後,使用的演算法不一樣了,總之加熵時uniqid更快了),重複率為0

  • 多程式版執行結果

探索 PHP 如何生成全域性唯一的 id

重複率為0,可以斷定一該方式生成唯一id可行,效能和重複概率表現很好,唯一不足就是id無序

2. 使用session_create_id()

該函式是php7.1之後提供的,是php用來生成session_id使用的,php使用它來生成每個請求會話,應該唯一是相當好的,不然呢!測試一波:

$random = session_create_id(); //長這樣子:6khfg75a13khre330nqu1t84ab
  • 單程式版執行結果

探索 PHP 如何生成全域性唯一的 id

  • 多程式版執行結果

探索 PHP 如何生成全域性唯一的 id

由上可見,session_create_id效能和重複率都表現不錯,值得信賴

3. 使用uuid

uuid已經形成的國際規範,目前出來的版本有四五個,最常用的是由時間戳+順序號+機器標識+程式標識規則生成的id,與此相關的composer包很多了,不造重車輪是最大的生產力,直接選用 https://packagist.org/packages/ramsey/uuid 包,測驗一下:

$random = Uuid::uuid1()->toString(); //長這樣子:480dac52-3102-11ea-89e3-525400cae48b
  • 單程式版執行結果

探索 PHP 如何生成全域性唯一的 id

  • 多程式版執行結果

探索 PHP 如何生成全域性唯一的 id

結果令人意外,uuid生成速度還行,但是多程式非同步生成時,並不能保證高標準的唯一性,出現了8個重複的,雖然很少,但是很意外,說好的全球唯一呢,有點虛,哈哈!總的來說還是不錯的,重複率已經做到相當低了。

4. 使用雪花演算法

以上三種都是隻會生成無序的隨機字元,但是我們有些時候需要依靠唯一id對資料庫進行排序的,比如我們要生成全庫唯一id時,我們就需要id滿足順序性、整數型別(提高索引效率),而由雪花演算法生成的id可以滿足這一點(雪花id有時間順序)。雪花演算法原理圖如下:

探索 PHP 如何生成全域性唯一的 id

安裝composer包:https://packagist.org/packages/godruoyi/ph... 來使用

$snowflake=new Snowflake(); 
$random = $snowflake->id(); //長這樣子:55141599398592512
  • 單程式版執行結果

探索 PHP 如何生成全域性唯一的 id

  • 多程式版執行結果

探索 PHP 如何生成全域性唯一的 id

分析結果,單程式使用雪花演算法生成id,可以保持十幾萬個每秒的生成速度,且重複率保持為0,但是去到併發生成時,問題出現了,重複了很多,重複率1/2,恐怖,可斷,雪花演算法如上面那樣簡單使用的話,是難以適合高併發場景的!

5.id計算器生成id

id計算器生成id就是的首要工作就是要設定一個公共變數,該變數增量為1,每次程式從這裡申請一次id,id的值都會加一,這樣一直累加下去,就保證了全域性唯一性,且都是整數。這方面,Redis很能勝任,一是效能很好,二是跨機器,另外Redis提供的原子性函式incr,簡直天造地設了。來,操作一波:

$random = Cache::inc('id');  //長這樣子:1,2,3,4,5,...
  • 單程式版執行結果

探索 PHP 如何生成全域性唯一的 id

  • 多程式版執行結果

探索 PHP 如何生成全域性唯一的 id

這種方式生成的id,能夠保持很好的id遞增性,即保證的順序性,由於incr是原子操作,重複率幾乎為0,產生速率,有賴於Redis的軟體效能、網路等等因素,也算是了比較好方案了。

以上各種id生成方式,各有各的優缺點,不能一概而論。要哪一種更好,還是要看具體需求,適合就是最好的。但總的來說,唯一性、效能是絕對高尚的。
除了上述幾種方式之外,還有很多生成唯一id的方案,也可以奇技淫巧一下(猥瑣發育),在上面幾種方式為基礎打造新的生成方式




原創不易,分享快樂,渴望動力

淺談併發加鎖

本作品採用《CC 協議》,轉載必須註明作者和本文連結

我只想看看藍天

相關文章