2009-08-10 15 views
12

Ich bin gespannt, gibt es eine Größenbeschränkung in PHP serialisieren. Wäre es möglich, ein Array mit 5.000 Schlüsseln und Werten zu serialisieren, damit es in einem Cache gespeichert werden kann?serialisieren Sie ein großes Array in PHP?

Ich hoffe, eine Benutzer-Freundesliste auf einer sozialen Netzwerk-Site zwischenzuspeichern, muss der Cache ziemlich oft aktualisiert werden, aber es muss fast jede Seitenladung gelesen werden.

Bei einem einzelnen Server Setup nehme ich an, APC wäre besser als Memcache für diese.

Antwort

27

Wie schon ein paar andere Leute bereits geantwortet haben, nur zum Spaß, hier ist eine sehr schnelle Benchmark (traue ich es so nennen?); Sehen Sie den folgenden Code:

$num = 1; 

$list = array_fill(0, 5000, str_repeat('1234567890', $num)); 

$before = microtime(true); 
for ($i=0 ; $i<10000 ; $i++) { 
    $str = serialize($list); 
} 
$after = microtime(true); 

var_dump($after-$before); 
var_dump(memory_get_peak_usage()); 

Ich laufe dies auf PHP 5.2.6 (die mit Ubuntu feschen gebündelt).
Und ja, es gibt nur Werte; keine Schlüssel; und die Werte sind ziemlich einfach: kein Objekt, kein Sub-Array, nichts anderes als String.

Für $num = 1 erhalten Sie:

float(11.8147978783) 
int(1702688) 

Für $num = 10 erhalten Sie:

float(13.1230671406) 
int(2612104) 

Und für $num = 100 erhalten Sie:

float(63.2925770283) 
int(11621760) 

So scheint es Je größer jedes Element des Arrays ist, desto länger Es dauert (scheint fair, eigentlich). Aber für Elemente 100 mal größer, nehmen Sie nicht 100 mal länger ...


nun mit einer Reihe von 50000 Elementen, anstelle von 5000, die diesen Teil des Codes bedeutet, wird geändert:

$list = array_fill(0, 50000, str_repeat('1234567890', $num)); 

Mit $num = 1 erhalten Sie:

float(158.236332178) 
int(15750752) 

In Anbetracht die Zeit, die es für 1 dauerte, werde ich nicht für entweder $ num = 10 noch $ num = 100 ...

ausführen Ja natürlich, in einer realen Situation würden Sie das nicht 10000 Mal tun; Versuchen wir es mit nur 10 Wiederholungen der for-Schleife.

Für $num = 1:

float(0.206310987473) 
int(15750752) 

Für $num = 10:

float(0.272629022598) 
int(24849832) 

Und für $num = 100:

float(0.895547151566) 
int(114949792) 

Ja, das ist fast 1 Sekunde - und ziemlich viel Speicher verwendet ^^
(Nein, das ist kein Produktionsserver: Ich habe eine ziemlich hohe memory_limit auf dieser Entwicklungsmaschine ^^)


Also, am Ende, ein bisschen kürzer als die Zahl zu sein - und, ja, können Sie Zahlen sagen, was Sie wollen, dass sie - würde ich nicht sagen, dass es eine „Grenze“, wie in „hartcodiert“ in PHP, aber Sie werden eine der Personen, die am Ende:

  • max_execution_time (im Allgemeinen auf einem Webserver sind es niemals mehr als 30 Sekunden)
  • memory_limit (auf einem Webserver ist es in der Regel nicht mehr als 32MB)
  • die Last, die Sie Webserver haben: während 1 dieser großen Serialisierung-Schleife ausgeführt wurde, dauerte es 1 meiner CPU; wenn Sie ganz ein paar Benutzer auf der gleichen Seite zur gleichen Zeit zu haben, lasse ich Sie sich vorstellen, was es ;-)
  • die Geduld des Benutzers geben ^^

Aber, es sei denn, wenn Sie sind wirklich Serialisierung langen Arrays von großen Daten, ich bin mir nicht sicher, es wird so viel Bedeutung ...
Und Sie müssen berücksichtigen, die Menge an Zeit/CPU-Auslastung mit diesem Cache könnte Ihnen helfen zu gewinnen ;-)

Dennoch wäre der beste Weg, um zu testen, mit echten Daten ;-)


Und Sie möchten vielleicht auch einen Blick auf das, was Xdebug nehmen tun können, wenn es um profiling kommt: diese Art von Situation ist einer von denen es für sinnvoll ist!

+0

+1 für den coolen "Benchmark". Interessant –

+0

Serialisierung der gleichen Daten des gleichen Datentyps immer wieder ist nicht gerade ein echter Benchmark. Plus, die tatsächlichen Kosten sind nicht Serialisierung, es ist unserializing. –

+1

True (deshalb war ich mir nicht sicher, ob ich es als Benchmark bezeichnen könnte ^^), und sehr wahr (weil es unserialisiert, wird das am meisten gemacht - sonst gibt es absolut keinen Grund, das in den Cache zu stellen) –

4

Die einzige praktische Grenze ist Ihr verfügbarer Speicher, da die Serialisierung die Erstellung einer Zeichenfolge im Speicher erfordert.

4

Es gibt kein durch PHP erzwungenes Limit. Serialize gibt eine Bytestream-Darstellung (Zeichenfolge) der serialisierten Struktur zurück, sodass Sie nur eine große Zeichenfolge erhalten.

6

Die Funktion serialize() ist nur durch den verfügbaren Speicher begrenzt.

3

Es gibt kein Limit, aber denken Sie daran, dass Serialisierung und Deserialisierung Kosten verursacht.

Die Unserialisierung ist extrem teuer.

Eine weniger teure Art und Weise von Caching, die Daten über var_export() als solche wäre (seit PHP 5.1.0, funktioniert es auf Objekte):

$largeArray = array(1,2,3,'hello'=>'world',4); 

file_put_contents('cache.php', "<?php\nreturn ". 
           var_export($largeArray, true). 
           ';'); 

Sie können dann einfach das Array abrufen, indem Sie die folgenden Aktionen ausführen :

$largeArray = include('cache.php'); 

Ressourcen können normalerweise nicht zwischengespeichert werden.

Wenn Sie kreisförmige Referenzen in Ihrem Array haben, müssen Sie leider serialize() verwenden.

+0

das hört sich gut an, außer ich bin mir nicht sicher in meinem Fall, würde dies wie 100.000 Dateien mit 100.000 Mitgliedern auf meiner Website erstellen, wenn ich sagte Cache sollte geklärten APC oder sonst Memcache. – JasonDavis

+0

Sie sollten das in Ihrem OP angegeben haben. –

+0

var_export würde eval() erfordern (was icky ist). es ist auch mindestens 3x langsamer zu var_export als es zu serialisieren ist, und var_export würde mehr post-serialisierten Speicher verwenden, da seine Datenstruktur nicht ganz so kompakt ist. – Justin

1

Nein, gibt es keine Begrenzung, und dies:

set_time_limit(0); 
ini_set('memory_limit ', -1); 

unserialize('s:2000000000:"a";'); 

Deshalb Sie sollten safe.mode haben = On oder eine Erweiterung wie Suhosin installiert ist, sonst wird es den gesamten Speicher in Ihrem System auffressen .

1

Ich denke besser als serialisieren ist json_encode Funktion. Es hat einen Nachteil, dass assoziative Arrays und Objekte nicht unterschieden werden, aber String-Ergebnis ist kleiner und leichter zu lesen von Mensch, so auch zu debuggen und zu bearbeiten.

+3

** json_encode: ** funktioniert gut für primitive Typen, aber sobald Sie Objekte haben, können Sie sie nicht verwenden, ohne die Datentreue zu verlieren. –

1

Wenn Sie es zwischenspeichern möchten (so nehme ich an, dass die Leistung das Problem ist), verwenden Sie stattdessen apc_add, um den Leistungseinbruch beim Konvertieren in einen String + Gain-Cache im Speicher zu vermeiden.

Wie oben erwähnt, ist die einzige Größenbeschränkung verfügbarer Speicher.

Ein paar andere Fehler: serialisiert Daten sind nicht tragbar zwischen Multi-Byte-und Single-Byte-Zeichenkodierungen. PHP5-Klassen enthalten NUL-Bytes, die Chaos mit Code verursachen können, der sie nicht erwartet.

1

Ihr Anwendungsfall klingt, als wären Sie besser dran, eine Datenbank zu verwenden, anstatt sich ausschließlich auf die verfügbaren Ressourcen von PHP zu verlassen. Die Vorteile bei der Verwendung von etwas wie MySQL ist, dass es speziell mit Speicherverwaltung für Dinge wie Speicher und Nachschlagen entwickelt wurde.

Es macht wirklich keinen Spaß, Daten ständig zu serialisieren und zu deserialisieren, nur um einige Informationen zu aktualisieren oder zu ändern.

2

Ok ... mehr Zahlen! (PHP 5.3.0 OSX, keine Opcode-Cache)

@ Pascal Code auf meinem Rechner für n = 1 bei 10k iters produziert:

float(18.884856939316) 
int(1075900) 

ich hinzufügen unserialize() auf die oben als so.

$num = 1; 

$list = array_fill(0, 5000, str_repeat('1234567890', $num)); 

$before = microtime(true); 
for ($i=0 ; $i<10000 ; $i++) { 
    $str = serialize($list); 
    $list = unserialize($str); 
} 
$after = microtime(true); 

var_dump($after-$before); 
var_dump(memory_get_peak_usage()); 

produziert

float(50.204112052917) 
int(1606768) 

ich die extra 600k annehmen oder so sind die serialisierten String.

Ich war neugierig auf var_export und seine include/eval Partner $str = var_export($list, true); statt serialize() in der ursprünglichen

float(57.064643859863) 
int(1066440) 

so nur ein wenig weniger Speicher (zumindest für dieses einfache Beispiel) erzeugt, aber viel mehr Zeit bereits.

Zugabe in eval('$list = '.$str.';'); statt unserialize in den obigen

produziert
float(126.62566018105) 
int(2944144) 

Anzeige Theres wahrscheinlich ein Speicherleck irgendwo, wenn eval tun: - /.

Also noch einmal, das sind keine großen Benchmarks (ich sollte das eval/unserialize isolieren, indem wir die Zeichenfolge in eine lokale Variable oder etwas einfügen, aber ich bin faul), aber sie zeigen die zugehörigen Trends. var_export scheint langsam zu sein.

+0

Danke für diese Tests! –

3

Wie oben von Thinker vorgeschlagen:

Sie

$string = json_encode($your_array_here); 

und ein Array

$array = json_decode($your_array_here, true); 

Dies gibt zu entschlüsseln verwenden könnte. Es funktioniert auch dann gut, wenn das codierte Array Multilevel ist.

+0

Ich muss hinzufügen, dass die Verwendung von json, um ein Array zu "serialisieren", oder "string-ify", auch Probleme verursachen, wenn Sie dort spezielle Zeichen haben. Zeichen wie "áéíóúñ" und ihre großgeschriebenen Versionen werden utf-8-codiert, es ist besondere Vorsicht geboten und es ist unklug, irgendeine Art von Zeichenfolgenüberprüfung auf einer nicht codierten json-Zeichenfolge auszuführen. Vor allem, wenn diese Validierung Dinge wie Schrägstriche, Anführungsstriche usw. ausführt. Das kann Ihr json-Objekt für die Funktion ** 'json_decode()' ** in PHP unbrauchbar machen, und es werden Fehler zurückgegeben. – EffectiX

+0

** 'json_decode()' ** dekodiert automatisch diese utf-8-codierten Zeichen. Solange der Schrägstrich vor dem codierten Zeichen nicht durcheinander ist, wird alles in Ordnung sein. (Sry. Lief im vorherigen Kommentar aus dem Leerzeichen.) – EffectiX

0

ich habe einen Fall, in dem unserialize eine Ausnahme auf einem großen serialisierte Objekt wirft, Größe: 65535 (die magische Zahl: 16 Bit-Voll-Bit = 65536)

0

Ich habe nur über eine Instanz kommen, wo ich dachte ich erreichte eine obere Grenze der Serialisierung.

Ich behalte serialisierte Objekte in einer Datenbank mit einem mysql TEXT Feld.

Die Grenze der verfügbaren Zeichen für ein Single-Byte-Zeichen ist 65.535, während ich viel größere Objekte als das mit PHP serialisieren kann. Es ist unmöglich, sie zu entserialisieren, da sie durch das Limit des Feldes TEXT abgeschnitten sind.