PHP uniqid函數執行緩慢的問題

來源:互聯網
上載者:User
前段時間某個需求:客戶提交簡單的表單可以建立一個適應於全終端(PC,Pad,Phone)的刮刮卡活動H5頁面,其中涉及到客戶可線上產生限額6W獎品碼的功能。

因為需要保持每個活動獎品碼的唯一,我們先是準備用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秒的時間產生,單次執行需要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 'more entropy' 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,'E','F','M','N','Q','K','Z');//一等獎的首碼            $level2Prefix =array(1,3,7,'A','C','J','R','U','V','X');//二等獎的前置            $level3Prefix = array(4,6,8,'B','D','G','H','I','L','O','P','R','S','T','W','Y');//三等獎的首碼            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.'A#1$v&'.$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);    }
  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.