PHP uniqid 함수의 느린 실행 문제

巴扎黑
풀어 주다: 2016-11-12 10:23:33
원래의
1271명이 탐색했습니다.

얼마 전 요구가 있었습니다: 고객이 한도를 생성할 수 있는 기능이 포함된 모든 단말기(PC, 패드, 휴대폰)에 적합한 스크래치 카드 활동을 위한 H5 페이지를 생성하기 위해 간단한 양식을 제출하십시오. 60,000개의 상품 코드가 온라인에 있습니다.

각 이벤트의 상품 코드를 고유하게 유지해야 하기 때문에 먼저 PHP의 uniqid 함수를 사용하여 알고리즘에 의해 생성된 전역 고유 식별자인 UUID(GUID라고도 불리는 Universal Unique Identifier)를 생성할 준비를 합니다. 식별자)를 생성합니다.

그러나 생성된 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초가 소요되며, 이는 생성하는 데 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 함수를 추가하면 이번에는 일치하는 데 거의 40초가 걸렸습니다. PHP 결과는 매번 다르게 생성된 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;
로그인 후 복사
드디어 39.99587739.995877이 걸리는 것으로 확인되었습니다. 1W 상금 코드를 생성하는 데 초가 소요되며 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);  
  
}
로그인 후 복사


관련 라벨:
php
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿