ホームページ  >  記事  >  バックエンド開発  >  PHPのuniqid関数の実行が遅い問題

PHPのuniqid関数の実行が遅い問題

巴扎黑
巴扎黑オリジナル
2016-11-21 10:23:581187ブラウズ

少し前の要件: 顧客は、すべての端末 (PC、パッド、電話) に適したスクラッチ カード アクティビティ用の H5 ページを作成するための簡単なフォームを送信します。これには、顧客が最大 60,000 個の賞品コードを生成できる機能が含まれます。オンライン。
各イベントの賞品コードを一意に保つ必要があるため、最初に PHP の uniqid 関数を使用して UUID (Universally Unique IDentifier、GUID とも呼ばれ、グローバルに一意の識別子、アルゴリズムによって生成される一意の識別子) を生成する準備をします。
しかし、以前に 1W のテストを生成したとき、データベースに挿入する時間を含まず、データの生成に数十秒かかることがわかりました。その後、パフォーマンス テスト用に xhprof を使用した簡単な例を作成しました

<?php
xhprof_enable(XHPROF_FLAGS_CPU|XHPROF_FLAGS_MEMORY);
function   myfunc(){
    for($i=0;$i<10000;$i++){
        $data = uniqid();
    }
}
myfunc();
$data = xhprof_disable();
print_r($data);

テスト結果:

[myfunc==>uniqid] => Array(
            [ct] => 10000
            [wt] => 39975062
            [cpu] => 0
            [mu] => 960752
            [pmu] => 0
)

実際には生成に 40 秒近くかかり、1 回の実行には 3969 マイクロ秒かかります。これは生成に 0.003969 秒かかります。ユーザーがフォームの送信と引き換えコードの生成を同時に行うと、最悪の場合、ユーザーへの応答に 4 分かかります。 もちろん、メッセージ キューを使用して非同期に生成することもできますが、uniqid はなぜそれほど時間がかかるのでしょうか。単純な文字列を生成するのに時間がかかるでしょうか?

次に、uniqid の実装ソースコードを確認してください。コードは以下に掲載されています

PHP_FUNCTION(uniqid)
{
     char *prefix = "";
#if defined(__CYGWIN__)
     zend_bool more_entropy = 1;
#else
     zend_bool more_entropy = 0;
#endif
     char *uniqid;
     int sec, usec, prefix_len = 0;
     struct timeval tv;
     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sb", &prefix, &prefix_len,
                                     &more_entropy)) {
          return;
     }
#if HAVE_USLEEP && !defined(PHP_WIN32)
     if (!more_entropy) {
#if defined(__CYGWIN__)
          php_error_docref(NULL TSRMLS_CC, E_WARNING, "You must use &#39;more entropy&#39; under CYGWIN");
          RETURN_FALSE;
#else
          usleep(1);
#endif
     }
#endif
     gettimeofday((struct timeval *) &tv, (struct timezone *) NULL);
     sec = (int) tv.tv_sec;
     usec = (int) (tv.tv_usec % 0x100000);
     /* The max value usec can have is 0xF423F, so we use only five hex
     * digits for usecs.
     */
     if (more_entropy) {
          spprintf(&uniqid, 0, "%s%08x%05x%.8F", prefix, sec, usec, php_combined_lcg(TSRMLS_C) * 10);
     } else {
          spprintf(&uniqid, 0, "%s%08x%05x", prefix, sec, usec);
     }
     RETURN_STRING(uniqid, 0);
}

ロジックを見ると、現在の時刻の秒とマイクロ秒を処理し、簡単なテストを書いているだけです

int getUniqid( char * uid) {
     int sec, usec;
     struct timeval tv;
     gettimeofday(( struct timeval *) &tv, ( struct timezone *) NULL);
     sec = ( int) tv. tv_sec;
     usec = ( int ) (tv.tv_usec % 0x100000);
     sprintf(uid, "%08x%05x" , sec, usec);
     return 1;
}

。 1W の実行には 2000 マイクロ秒しかかかりません。これはなぜですか?ただし、生成された uid に重複が多いことが判明したため、元のコードの usleep 関数に注目しました
テストに usleep 関数を追加しました。今回は、PHP の結果と一致するまでに 40 秒近くかかりました。 . usleep はすべてを保持するためにここにあります。 今回生成された uid は異なります。
問題が発生したのはusleep関数で、usleepの前後にインターバル時間を加えたコードは以下の通りです

struct timeval start, end;
      gettimeofday(( struct timeval *) &start, ( struct timezone *) NULL);
      usleep(1);
      gettimeofday(( struct timeval *) &end, ( struct timezone *) NULL);
      unsigned long space = (end.tv_sec - start. tv_sec) * 1000000 + end.tv_usec
              - start. tv_usec;
       spaceCost += space;

最終的に、1Wの賞品コードを生成するのに39.99587739.995877秒かかることが分かりました。 usleep 間隔時間は 39.982442m です。これは usleep 時間を出力することでわかりました。usleep(1) がプロセスの停止から復帰までに 4000 マイクロ秒かかるたびに、usleep では精度が達成できず、その差が大きすぎることがわかります。
最後に、次のコードを使用して賞品コードを生成しました

/**
     * 生成兑换码并保存到数据库  返回setNo
     * $pageId 活动ID
     * $level 奖品等级
     * $numbers 生成奖品的个数
     */
    public static  function generateCDKEYAndSave($pageId,$level,$numbers){
        $level1Prefix =array(2,5,9,&#39;E&#39;,&#39;F&#39;,&#39;M&#39;,&#39;N&#39;,&#39;Q&#39;,&#39;K&#39;,&#39;Z&#39;);//一等奖的前缀
        $level2Prefix =array(1,3,7,&#39;A&#39;,&#39;C&#39;,&#39;J&#39;,&#39;R&#39;,&#39;U&#39;,&#39;V&#39;,&#39;X&#39;);//二等奖的前置
        $level3Prefix = array(4,6,8,&#39;B&#39;,&#39;D&#39;,&#39;G&#39;,&#39;H&#39;,&#39;I&#39;,&#39;L&#39;,&#39;O&#39;,&#39;P&#39;,&#39;R&#39;,&#39;S&#39;,&#39;T&#39;,&#39;W&#39;,&#39;Y&#39;);//三等奖的前缀
        if(empty($pageId) || empty($level) || empty($numbers)) return false;
        $levelPrefix =$level1Prefix;
        if($level==2) $levelPrefix = $level2Prefix;
        if($level==3) $levelPrefix = $level3Prefix;
        $codes =array();
        $now = time();
        for($i=0;$i<$numbers;$i++){
            $prefixKey = array_rand($levelPrefix);
            $prefix = self::COUPON_PREFIX.$levelPrefix[$prefixKey];
            //$code =base_convert(hexdec(md5(uniqid())),10,26); 服务器上面uniqid执行慢的要死
            //$code =base_convert(hexdec(md5($pageId.&#39;A#1$v&&#39;.$i)),10,26);//数据过多 hexdec丢失大量精度
            $code1 = base_convert(substr(md5($pageId.$i.$now), 0, 10), 16, 36);
            $code2 = base_convert($i, 10, 26);
            $code2Len = strlen($code2);
            if ($code2Len == 1) {
                $code2 .= chr(rand(82, 90)) . chr(rand(82, 90)) . chr(rand(82, 90));
            }
            else if ($code2Len == 2) {
                $code2 .= chr(rand(82, 90)) . chr(rand(82, 90));
            }
            else if ($code2Len == 3) {
                $code2 .= chr(rand(82, 90));
            }
            $code =$code1.$code2;
            $codes[] = $prefix.strtoupper($code);
        }
       return $codes;
    }

uuid テストコードが付属しています

#include <stdio.h>
#include <malloc.h>
#include <sys/time.h>
#include <unistd.h>
unsigned long sleepCost = 0;
int getUniqid( char * uid,int times) {
      struct timeval start, end;
      gettimeofday(( struct timeval *) &start, ( struct timezone *) NULL);
      usleep(1);
      gettimeofday(( struct timeval *) &end, ( struct timezone *) NULL);
      unsigned long space = (end.tv_sec - start. tv_sec) * 1000000 + end.tv_usec
              - start. tv_usec;
     sleepCost += space;
      if (0 == times%1000) printf ("\n-----sleep cost-------\n%lu usec\n", space);
      int sec, usec;
      struct timeval tv;
      gettimeofday(( struct timeval *) &tv, ( struct timezone *) NULL);
     sec = ( int) tv. tv_sec;
     usec = ( int ) (tv.tv_usec % 0x100000);
      sprintf(uid, "%08x%05x" , sec, usec);
      return 1;
}
int main( int argc, char * argv[]) {
      struct timeval start, end;
      gettimeofday(( struct timeval *) &start, ( struct timezone *) NULL);
      for ( int i = 1; i <= 10000; i++) {
           char data[20];
          getUniqid(data,i);
     }
      gettimeofday(( struct timeval *) &end, ( struct timezone *) NULL);
      unsigned long space = (end.tv_sec - start. tv_sec) * 1000000 + end.tv_usec
              - start. tv_usec;
      printf( "\n-----cost-------\n% lu usec\n  \n-----sum sleep sost-------\n% lu usec\n" , space,sleepCost);
}


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。