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

巴扎黑
Freigeben: 2016-11-21 10:23:58
Original
1173 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, 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);
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?

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

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

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,&#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
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!