얼마 전 요구가 있었습니다: 고객이 한도를 생성할 수 있는 기능이 포함된 모든 단말기(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 '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 함수를 추가하면 이번에는 일치하는 데 거의 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,'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); }