欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

PHP uniqid函数执行缓慢的有关问题

程序员文章站 2022-05-01 19:02:30
...
PHP uniqid函数执行缓慢的问题
前段时间某个需求:客户提交简单的表单可以创建一个适应于全终端(PC,Pad,Phone)的刮刮卡活动H5页面,其中涉及到客户可在线生成限额6W奖品码的功能。

因为需要保持每个活动奖品码的唯一,我们先是准备用PHP的uniqid函数来生成UUID(Universally Unique IDentifier,也叫GUID,为全局唯一标识符,是一种由算法生成的唯一标识)来生成。

但当我们用生成1W测试时候,发现生成过些需要几十秒,还不包括插入至数据库的时间,然后用xhprof写了个简单例子进行性能测试

测试结果:

[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
附带uuid测试代码
#include #include #include #include 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 
PHP uniqid函数执行缓慢的有关问题

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。

相关文章

相关视频