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, eine global eindeutige Kennung, die eine von einem Algorithmus generierte eindeutige Kennung ist) zu generieren erzeugen.
Aber als wir früher 1W zum Testen generierten, stellten wir fest, dass es mehrere zehn Sekunden dauerte, etwas 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?
Überprüfen Sie dann den Implementierungsquellcode von uniqid. Der Code ist unten aufgeführt.
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, gibt es keine komplizierten Vorgänge in der aktuellen Zeit Sekunden und Mikrosekunden, und dann schreibe ich 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; }
und es dauert 2000 Mikrosekunden, um 1W mal 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 ist in der Funktion „usleep“ aufgetreten. Fügen Sie 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 die Generierung 39,99587739,995877 Sekunden dauert Der 1W-Preiscode und die gesamte Usleep-Intervallzeit betragen 39,982442 m. Durch das Drucken der Usleep-Zeit haben wir festgestellt, dass usleep(1) jedes Mal 4000 Mikrosekunden von der Prozessunterbrechung bis zum Aufwachen benötigt. Wir wussten bereits, dass usleep die Genauigkeit nicht erreichen konnte , und der Unterschied war 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); }