2009-08-10 14 views
12

Sono curioso, esiste un limite di dimensione per la serializzazione in PHP. Sarebbe possibile serializzare un array con 5.000 chiavi e valori in modo che possa essere memorizzato in una cache?serializza un grande array in PHP?

Spero di memorizzare nella cache una lista di amici su un sito di social network, la cache dovrà essere aggiornata abbastanza spesso, ma dovrà essere letta quasi ogni caricamento di pagina.

In una configurazione di singolo server, presumo che APC sarebbe meglio di Memcache per questo.

risposta

27

Come un bel paio di altre persone hanno già risposto, solo per divertimento, ecco un benchmark molto veloce (oserei chiamarlo così?); considerare il seguente codice:

$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()); 

Io corro questo su PHP 5.2.6 (quella in bundle con Ubuntu sbarazzino).
E, sì, ci sono solo valori; niente chiavi; e i valori sono abbastanza semplici: nessun oggetto, nessun sotto-array, niente altro che una stringa.

Per $num = 1, si ottiene:

float(11.8147978783) 
int(1702688) 

Per $num = 10, si ottiene:

float(13.1230671406) 
int(2612104) 

E, per $num = 100, si ottiene:

float(63.2925770283) 
int(11621760) 

Così, sembra più grande è ogni elemento dell'array, il più lungo er ci vuole (sembra giusto, in realtà). Ma per elementi 100 volte più grandi, non prendi 100 volte di più ...


Ora, con una serie di 50000 elementi, invece di 5000, il che significa che questa parte del codice viene cambiato:

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

Con $num = 1, si ottiene:

float(158.236332178) 
int(15750752) 

Considerando il tempo necessario per 1, non eseguirò questo per $ num = 10 né $ num = 100 ...


Sì, certo, in una situazione reale, non lo faresti 10000 volte; quindi proviamo con solo 10 iterazioni del ciclo for.

Per $num = 1:

float(0.206310987473) 
int(15750752) 

Per $num = 10:

float(0.272629022598) 
int(24849832) 

E per $num = 100:

float(0.895547151566) 
int(114949792) 

Sì, è quasi 1 secondo - e un po 'di memoria utilizzata ^^
(No, questo non è un server di produzione: ho una piuttosto elevata memory_limit su questa macchina di sviluppo ^^)


Così, alla fine, di essere un po 'più brevi di quelli numero - e, Sì, è possibile avere numeri dicono tutto quello che vuoi che - non direi c'è un "limite" come in "hardcoded" in PHP, ma si finirà per fronte a una di quelle:

  • max_execution_time (in genere, su un server web, non è mai più di 30 secondi)
  • memory_limit (su un server web, in genere non è muco più di 32 MB)
  • il carico che il server web avrà: mentre uno di quei grandi serialize-loop era in esecuzione, ci voleva 1 della mia CPU; se si hanno un bel paio di utenti sulla stessa pagina nello stesso momento, vi lascio immaginare che cosa darà ;-)
  • la pazienza del vostro utente ^^

Ma, tranne se stanno davvero serializzando gli array lunghi di , non sono sicuro che sarà così importante ...
E si deve prendere in considerazione la quantità di tempo/carico della CPU usando quella cache potrebbe aiutarti a guadagnare ;-)

Ancora, il modo migliore per sapere sarebbe quello di testare da soli, con dati reali ;-)


E si potrebbe anche voler dare un'occhiata a ciò che Xdebug può fare quando si tratta di profiling: questo tipo di situazione è uno di quelli è utile per!

+0

+1 per il "benchmark" cool. Interessante –

+0

La serializzazione degli stessi dati dello stesso tipo di dati più e più volte non è esattamente un vero punto di riferimento. Inoltre, il costo reale non è la serializzazione, è poco statistico. –

+1

Vero (è per questo che non ero sicuro di poterlo chiamare un punto di riferimento ^^), e molto vero (perché non sta valutando che verrà fatto di più - altrimenti, non c'è assolutamente alcun motivo per metterlo nella cache) –

4

L'unico limite pratico è la memoria disponibile, poiché la serializzazione comporta la creazione di una stringa in memoria.

4

Nessun limite applicato da PHP. Serialize restituisce una rappresentazione in bytestream (stringa) della struttura serializzata, in modo da ottenere solo una stringa di grandi dimensioni.

6

La funzione serialize() è limitata solo dalla memoria disponibile.

3

Non c'è limite, ma ricorda che la serializzazione e la serializzazione hanno un costo.

La unserializzazione è estremamente costosa.

Un modo meno costoso di caching che i dati sarebbero stati via var_export() come tale (da PHP 5.1.0, funziona sugli oggetti):

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

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

Potete poi semplicemente recuperare la matrice nel modo seguente :

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

Le risorse in genere non sono in grado di memorizzare la cache.

Sfortunatamente, se si dispone di riferimenti circolari nell'array, sarà necessario utilizzare serialize().

+0

questo suona bene, tranne che non sono sicuro nel mio caso, questo creerebbe come 100.000 file con 100.000 membri sul mio sito, quando ho detto cache dovrei di APC chiarito o memcache. – JasonDavis

+0

Dovresti specificarlo nel tuo OP. –

+0

var_export richiederebbe eval() (che è icky). è anche almeno 3x più lento a var_export di quanto non lo sia per la serializzazione, e var_export userebbe più memoria post-serializzata dal suo datastructure non proprio così compatto. – Justin

1

No, non c'è limite e questo:

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

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

è il motivo per cui si dovrebbe avere safe.mode = On o un'estensione simile Suhosin installato, altrimenti si mangia tutta la memoria del sistema .

1

Penso che migliore di serializzare è la funzione json_encode. Ha uno svantaggio, che gli array e gli oggetti associativi non sono distinti, ma il risultato della stringa è più piccolo e più facile da leggere da parte umana, quindi anche il debug e la modifica.

+3

** json_encode: ** funziona bene per i tipi primitivi, ma non appena si dispone di oggetti, non è possibile utilizzarlo senza perdere la fedeltà dei dati. –

1

Se si desidera memorizzarlo nella cache (quindi presumo che le prestazioni siano il problema), utilizzare invece apc_add per evitare il problema di prestazioni della conversione in una stringa + guadagno di cache in memoria.

Come indicato sopra, l'unica dimensione limite è la memoria disponibile.

Alcuni altri trucchi: i dati serializzati non sono portatili tra codifiche di caratteri a byte singolo e multibyte. Le classi PHP5 includono NUL byte che possono causare il caos con codice che non li prevede.

1

Il tuo caso d'uso sembra che tu stia meglio usando un database per farlo invece di affidarti unicamente alle risorse disponibili di PHP. I vantaggi nell'usare qualcosa come MySQL è che è stato progettato appositamente per la gestione della memoria per cose come l'archiviazione e la ricerca.

Non è affatto divertente serializzare e serializzare costantemente i dati solo per aggiornare o modificare alcune informazioni.

2

Ok ... altri numeri! (PHP 5.3.0 OSX, nessuna cache codice operativo)

@ codice di Pascal sulla mia macchina per n = 1 a 10k iter produce:

float(18.884856939316) 
int(1075900) 

unserialize() aggiungo a quanto sopra come così.

$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()); 

produce

float(50.204112052917) 
int(1606768) 

Suppongo che il 600k in più sono la stringa serializzata.

mi incuriosiva var_export e la sua includono/partner di eval $str = var_export($list, true); invece di serialize() in originale produce

float(57.064643859863) 
int(1066440) 

quindi solo un po 'meno memoria (almeno per questo semplice esempio), ma molto più tempo già.

aggiungendo eval('$list = '.$str.';'); invece unserialize nel precedente produce

float(126.62566018105) 
int(2944144) 

theres d'indicazione probabilmente una perdita di memoria da qualche parte quando si fa eval: - /.

Quindi, di nuovo, questi non sono grandi punti di riferimento (dovrei davvero isolare eval/unserialize mettendo la stringa in una var locale o qualcosa del genere, ma sono pigro) ma mostrano le tendenze associate. var_export sembra lento.

+0

Grazie per questi test! –

3

come suggerito dal Pensatore sopra:

Si potrebbe utilizzare

$string = json_encode($your_array_here); 

e per decodificarlo

$array = json_decode($your_array_here, true); 

Ciò restituisce un array. Funziona bene anche se l'array codificato era multilivello.

+0

Devo aggiungere che usare json per 'serializzare' un array, o 'string-ify', causerà anche problemi se ci sono caratteri speciali. Personaggi come: "áéíóúñ" e le loro versioni in maiuscolo otterranno la codifica utf-8, si dovrà prestare particolare attenzione e l'esecuzione di qualsiasi tipo di convalida delle stringhe su una stringa json non codificata è sconsigliata. Soprattutto se detta convalida fa cose come lo slash stripping, il pasticcio con le virgolette, ecc. Questo può rendere inutilizzabile l'oggetto json per la funzione ** 'json_decode()' ** in php, e restituirà degli errori. – EffectiX

+0

** 'json_decode()' ** decodifica automaticamente questi caratteri codificati utf-8. Quindi, fintanto che lo slash che precede il carattere codificato non viene incasinato, andrà tutto bene. (Sry. Ha spaziato nello spazio nel commento precedente). – EffectiX

0

Ho un caso in cui unserialize genera un'eccezione su un grande oggetto serializzato, dimensioni: 65535 (il numero magico: 16 bit po 'pieno = 65536)

0

ho appena incontrato un caso in cui ho pensato che stava raggiungendo un limite superiore di serializzazione.

Sto persistendo oggetti serializzati in un database utilizzando un campo mysql TEXT.

Il limite dei caratteri disponibili per un carattere a byte singolo è 65.535, quindi mentre posso serializzare oggetti molto più grandi di quello con PHP È impossibile digitalizzarli come sono troncati dal limite del campo TEXT.