>백엔드 개발 >PHP 튜토리얼 >PHP uniqid 함수의 느린 실행 문제

PHP uniqid 함수의 느린 실행 문제

巴扎黑
巴扎黑원래의
2016-11-21 10:23:581219검색

얼마 전 요구가 있었습니다: 고객이 한도를 생성할 수 있는 기능이 포함된 모든 단말기(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초가 걸립니다. 이는 생성하는 데 0.003969초에 해당하는 3969마이크로초입니다. 사용자가 양식을 제출하고 동시에 상환 코드를 생성하면 최악의 경우 사용자에게 응답하는 데 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);
}


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.