Heim > Artikel > Backend-Entwicklung > Das Problem der langsamen Ausführung der PHP-Uniqid-Funktion
Vor einiger Zeit gab es die Forderung: Kunden reichen ein einfaches Formular ein, um eine H5-Seite für Rubbellosaktivitäten zu erstellen, die für alle Endgeräte (PC, Pad, Phone) geeignet ist und die Funktion beinhaltet, dass Kunden ein Limit von 60.000 generieren können Preiscodes online.
Da wir den Preiscode jeder Veranstaltung einzigartig halten müssen, bereiten wir uns zunächst darauf vor, die Uniqid-Funktion von PHP zu verwenden, um eine UUID (Universally Unique IDentifier, auch GUID genannt) zu generieren, bei der es sich um eine weltweit eindeutige Kennung handelt, die von einem Algorithmus generiert wird. Unique Identifikator) zu generieren.
Aber als wir den generierten 1W-Test verwendeten, stellten wir fest, dass es mehrere zehn Sekunden dauerte, einige zu generieren, ohne die Zeit zum Einfügen in die Datenbank. Dann haben wir ein einfaches Beispiel mit xhprof für Leistungstests geschrieben
<?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);
Testergebnisse:
[myfunc==>uniqid] => Array( [ct] => 10000 [wt] => 39975062 [cpu] => 0 [mu] => 960752 [pmu] => 0 )
Die Generierung dauert tatsächlich fast 40 Sekunden. Eine einzelne Ausführung dauert 3969 Mikrosekunden, was 0,003969 Sekunden entspricht. Wenn der Benutzer das Formular abschickt und gleichzeitig einen Einlösungscode generiert, dauert es im schlimmsten Fall 4 Minuten, bis er dem Benutzer antwortet. Natürlich kann er auch asynchron über die Nachrichtenwarteschlange generiert werden, aber warum dauert das bei Uniqid? viel Zeit, um eine einfache Zeichenfolge zu generieren?
Dann schauen Sie sich den Implementierungsquellcode von uniqid an.
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); }
Wenn man sich die Logik ansieht, verarbeitet er einfach die aktuellen Zeitsekunden und Mikrosekunden. und schreibt dann einen einfachen Test
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; }
, der 2000 Mikrosekunden benötigt, um 1 W auszuführen. Warum ist das so? Wir haben jedoch festgestellt, dass es in der generierten UID viele Duplikate gab, daher haben wir auf die Funktion usleep im Originalcode geachtet.
Beim Hinzufügen der Funktion usleep im Test dauerte es dieses Mal fast 40 Sekunden, bis sie mit der UID übereinstimmte Das PHP-Ergebnis dient dazu, die generierte UID jedes Mal unterschiedlich zu halten.
Das Problem trat in der usleep-Funktion auf und fügte dann die Intervallzeit vor und nach usleep hinzu. Der Code lautet wie folgt
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;
Schließlich wurde festgestellt, dass es 39,99587739,995877 dauert Sekunden zum Generieren des 1W-Preiscodes und usleep Die Gesamtintervallzeit beträgt 39,982442 m. Durch Drucken der usleep-Zeit wird festgestellt, dass es von der Unterbrechung des Prozesses bis zum Aufwachen jedes Mal dauert, wenn usleep(1). usleep kann die Genauigkeit nicht erreichen und der Unterschied ist zu groß.
Schließlich wurde der folgende Code verwendet, um den Preiscode zu generieren
/** * 生成兑换码并保存到数据库 返回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; }
Kommt mit UUID-Testcode
#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); }