Das Problem der langsamen Ausführung der PHP-Uniqid-Funktion

巴扎黑
Freigeben: 2016-11-12 10:23:33
Original
1268 Leute haben es durchsucht

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);
Nach dem Login kopieren

Testergebnisse:

[myfunc==>uniqid] => Array(  
            [ct] => 10000  
            [wt] => 39975062  
            [cpu] => 0  
            [mu] => 960752  
            [pmu] => 0  
)
Nach dem Login kopieren

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 &#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);  
}
Nach dem Login kopieren

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;  
}
Nach dem Login kopieren

, 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;
Nach dem Login kopieren

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,&#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;  
  
    }
Nach dem Login kopieren


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);  
  
}
Nach dem Login kopieren


Verwandte Etiketten:
php
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage