2009-08-10 9 views
12

Tengo curiosidad, existe un límite de tamaño para serializar en PHP. ¿Sería posible serializar una matriz con 5.000 claves y valores para que pueda almacenarse en una memoria caché?serializar una gran matriz en PHP?

Espero cachear la lista de amigos de un usuario en un sitio de red social, la memoria caché deberá actualizarse con bastante frecuencia, pero deberá leerse casi cada carga de página.

En una configuración de servidor único, supongo que APC sería mejor que Memcache para esto.

Respuesta

27

Como muchas otras personas ya respondieron, solo por diversión, aquí hay un punto de referencia muy rápido (¿me atrevo a llamarlo así?); considere el siguiente código:

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

Me estoy quedando esto en PHP 5.2.6 (el que está incluido con Ubuntu vivaces).
Y, sí, solo hay valores; sin llaves; y los valores son bastante simples: ningún objeto, ninguna sub-matriz, nada de cadenas.

Para $num = 1, se obtiene:

float(11.8147978783) 
int(1702688) 

Para $num = 10, se obtiene:

float(13.1230671406) 
int(2612104) 

Y, por $num = 100, se obtiene:

float(63.2925770283) 
int(11621760) 

Por lo tanto, parece cuanto más grande es cada elemento de la matriz, el largo se necesita (parece justo, en realidad). Pero, para elementos 100 veces más grandes, no tomas 100 veces más tiempo ...


Ahora, con una serie de 50000 elementos, en lugar de 5000, lo que significa que esta parte del código se cambia:

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

Con $num = 1, se obtiene:

float(158.236332178) 
int(15750752) 

Teniendo en cuenta el tiempo que tardó en 1, no ejecutaré esto ni para $ num = 10 ni $ num = 100 ...


Sí, por supuesto, en una situación real, no estarías haciendo esto 10000 veces; intentemos con solo 10 iteraciones del ciclo for.

Para $num = 1:

float(0.206310987473) 
int(15750752) 

Para $num = 10:

float(0.272629022598) 
int(24849832) 

Y para $num = 100:

float(0.895547151566) 
int(114949792) 

Sí, eso es casi 1 segundo - y un poco de memoria utilizada ^^
(No, esto no es un servidor de producción: Tengo una muy alta memory_limit en esta máquina desarrollo ^^)


Así que, al final, a ser un poco más cortos que los número - y, Sí, puede hacer que los números dicen lo que usted quiere que - no diría que hay un "límite", como en "codificado" en PHP, pero vas a acabar frente a uno de esos:

  • max_execution_time (en general, en un servidor web, nunca es más de 30 segundos)
  • memory_limit (en un servidor web, en general no es muco más de 32 MB)
  • carga que tendrá el servidor web: mientras se ejecutaba 1 de esos grandes serialize-loop, tomó 1 de mi CPU; si tiene un buen par de usuarios en la misma página al mismo tiempo, te dejo imaginar lo que dará ;-)
  • la paciencia de su usuario ^^

Pero, salvo si están realmente serializando matrices largas de datos grandes, no estoy seguro de que importe tanto ...
Y debe tener en cuenta la cantidad de tiempo/carga de la CPU que utiliza ese caché puede ayudarle a ganar ;-)

Aún así, la mejor manera de saberlo sería probarlo usted mismo, con datos reales ;-)


Y también puede consultar lo que Xdebug puede hacer cuando se trata de profiling: este tipo de situación es una de las que es útil.

+0

+1 para el "punto de referencia" genial. Interesante –

+0

La serialización de los mismos datos del mismo tipo de datos una y otra vez no es exactamente un punto de referencia real. Además, el costo real no es la serialización, es la deserialización. –

+1

Es cierto (es por eso que no estaba seguro de poder llamarlo un punto de referencia ^^), y muy cierto (porque es la deserialización lo que más se hará, de lo contrario, no hay absolutamente ninguna razón para ponerlo en el caché) –

4

El único límite práctico es su memoria disponible, ya que la serialización implica la creación de una cadena en la memoria.

4

PHP no impone límites. Serialize devuelve una representación de bitcool (cadena) de la estructura serializada, por lo que solo obtendría una cadena grande.

6

La función serialize() solo está limitada por la memoria disponible.

3

No hay límite, pero recuerde que la serialización y la deserialización tienen un costo.

La deserialización es extremadamente costosa.

Una manera menos costosa de almacenamiento en caché que los datos serían través var_export() como tal (a partir de PHP 5.1.0, funciona en objetos):

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

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

continuación, puede simplemente recuperar la matriz de la siguiente :

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

Los recursos no suelen almacenarse en caché.

Desafortunadamente, si tiene referencias circulares en su matriz, necesitará usar serialize().

+0

esto suena bien, excepto que no estoy seguro en mi caso, esto crearía como 100.000 archivos con 100.000 miembros en mi sitio, cuando dije caché debería haber aclarado APC o de lo contrario Memcache. – JasonDavis

+0

Debe especificar eso en su OP. –

+0

var_export requeriría eval() (que es asqueroso). también es al menos 3 veces más lenta que var_export de lo que es serializar, y var_export usaría más memoria post-serializada ya que no es una estructura de datos tan compacta. – Justin

1

No, no hay límite y esto:

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

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

es por eso que debe tener safe.mode = El o como una extensión Suhosin instalado, de lo contrario se va a comer toda la memoria en su sistema .

1

Creo que mejor que serializar es json_encode función. Tiene un inconveniente, que las matrices asociativas y los objetos no se distinguen, pero el resultado de la cadena es más pequeño y más fácil de leer por los humanos, por lo que también se puede depurar y editar.

+3

** json_encode: ** funciona bien para los tipos primitivos, pero tan pronto como tenga objetos, no podrá usarlos sin perder la fidelidad de los datos. –

1

Si desea almacenar en caché (así que supongo que el rendimiento es el problema), utilice apc_add en su lugar para evitar el golpe de rendimiento de convertirlo en una cadena + ganar memoria caché en la memoria.

Como se mencionó anteriormente, el único límite de tamaño disponible es la memoria.

Algunas otras capturas: datos serializados no son portátiles entre codificaciones de caracteres de bytes múltiples y de un solo byte. Las clases PHP5 incluyen bytes NUL que pueden causar estragos con código que no los espera.

1

Su caso de uso parece que es mejor utilizar una base de datos para hacer eso en lugar de depender únicamente de los recursos disponibles de PHP. Las ventajas de utilizar algo como MySQL en su lugar es que está diseñado específicamente con la gestión de la memoria en mente para cosas como el almacenamiento y la búsqueda.

Realmente no es divertido serializar y desesterizar datos constantemente solo para actualizar o cambiar algunas piezas de información.

2

Ok ... más números! (PHP 5.3.0 OSX, sin caché de código de operación)

@ código de Pascal en mi máquina para n = 1 a 10K ITERS produce:

float(18.884856939316) 
int(1075900) 

agrego unserialize() a lo anterior como tal.

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

supongo que el 600k adicional o también lo son la cadena serializada.

Tenía curiosidad acerca de var_export y su include/socio eval $str = var_export($list, true); en lugar de serializar() en el original produce

float(57.064643859863) 
int(1066440) 

tan sólo un poco menos memoria (al menos para este sencillo ejemplo) pero mucho más tiempo ya.

adición en lugar de eval('$list = '.$str.';'); unserialize en el anterior produce

float(126.62566018105) 
int(2944144) 

theres que indican probablemente una pérdida de memoria cuando se hace alguna parte eval: - /.

De nuevo, estos no son excelentes puntos de referencia (realmente debería aislar el eval/unserialize poniendo la cadena en una var local o algo así, pero estoy siendo flojo) pero muestran las tendencias asociadas. var_export parece lento.

+0

¡Gracias por esas pruebas! –

3

según lo sugerido por encima de pensador:

Usted podría utilizar

$string = json_encode($your_array_here); 

y decodificarlo

$array = json_decode($your_array_here, true); 

Esto devuelve una matriz. Funciona bien incluso si la matriz codificada era multinivel.

+0

Debo agregar que usar json para 'serializar' una matriz, o 'string-ify', también causará problemas si tiene caracteres especiales allí. Caracteres como: "áéíóúñ" y sus versiones en mayúsculas tendrán codificación UTF-8, se debe tener especial cuidado, y ejecutar cualquier tipo de validación de cadena en una cadena json no codificada no es aconsejable. Especialmente si dicha validación hace cosas como quitar barras, desordenar las comillas, etc. Esto puede dañar el objeto json inútil para la función ** 'json_decode()' ** en php, y devolverá errores. – EffectiX

+0

** 'json_decode()' ** decodifica automáticamente estos caracteres codificados en utf-8. Por lo tanto, siempre que la barra anterior al carácter codificado no se altere, todo estará bien. (Sry. Se escapó del espacio en el comentario anterior). – EffectiX

0

Tengo un caso en el que unserialize se produce una excepción en un objeto serializado grande, tamaño: 65535 (el número mágico: poco lleno 16bits = 65536)

0

acabo de venir a través de una instancia donde pensé que estaba llegando a un límite superior de serialización.

Estoy persistiendo objetos serializados a una base de datos usando un campo mysql TEXT.

El límite de caracteres disponibles para caracteres de un solo byte es de 65.535, así que aunque puedo serializar objetos mucho más grandes que eso con PHP, es imposible deserializarlos ya que están truncados por el límite del campo TEXT.