Método preferido para almacenar matrices PHP (json_encode vs serialize)

Necesito almacenar una matriz asociativa multidimensional de datos en un archivo plano para fines de almacenamiento en caché. De vez en cuando puedo encontrar la necesidad de convertirlo a JSON para utilizarlo en mi aplicación web, pero la gran mayoría de las veces voy a usar la matriz directamente en PHP.

¿Sería más eficiente almacenar la matriz como JSON o como una matriz serializada de PHP en este archivo de texto? He mirado alrededor y parece que en las versiones más nuevas de PHP (5.3), json_decode es realmente más rápido que unserialize .

Actualmente me estoy inclinando por el almacenamiento de la matriz como JSON, ya que creo que es más fácil de leer por un humano si es necesario, se puede usar tanto en PHP como en JavaScript con muy poco esfuerzo, y por lo que he leído, incluso podría ser más rápido para decodificar (sin embargo, no estoy seguro acerca de la encoding).

¿Alguien sabe de algún escollo? ¿Alguien tiene buenos puntos de referencia para mostrar los beneficios de rendimiento de cualquiera de los métodos?

Depende de tus prioridades

Si el rendimiento es su característica de conducción absoluta, entonces use la más rápida. Solo asegúrate de tener una comprensión completa de las diferencias antes de tomar una decisión

  • De forma predeterminada, json_encode() convierte los caracteres UTF-8 en secuencias de escape Unicode, mientras que serialize() no lo hace. Nota: Para dejar intactos los caracteres UTF-8, puede usar la opción JSON_UNESCAPED_UNICODE partir de PHP 5.4.
  • JSON no tendrá memoria de la clase original del objeto (siempre se restauran como instancias de stdClass).
  • No puede aprovechar __sleep() y __wakeup() con JSON
  • Solo las propiedades públicas se serializan con JSON. Nota: A partir de PHP 5.4, puede implementar JsonSerializable para cambiar este comportamiento.
  • JSON es más portátil

Y probablemente haya otras diferencias en las que no puedo pensar en este momento.

Una prueba de velocidad simple para comparar los dos

  

JSON es más simple y más rápido que el formato de serialización de PHP y debe usarse a menos que :

  • Está almacenando matrices profundamente anidadas: json_decode() : “Esta función devolverá falso si los datos codificados JSON son más profundos que 127 elementos”.
  • Está almacenando objetos que deben ser deserializados como la clase correcta
  • Estás interactuando con versiones antiguas de PHP que no son compatibles con json_decode

Escribí un blog sobre este tema: ” Caché una gran matriz: JSON, serialize o var_export? “. En esta publicación, se muestra que serializar es la mejor opción para arreglos de tamaño pequeño a grande. Para matrices muy grandes (> 70 MB), JSON es la mejor opción.

También podría estar interesado en https://github.com/phadej/igbinary , que proporciona un ‘motor’ de serialización diferente para PHP.

Mis cifras de “rendimiento” arbitrario / arbitrario, utilizando PHP 5.3.5 en una plataforma de 64 bits muestran:

JSON:

  • JSON codificado en 2.180496931076 segundos
  • JSON decodificado en 9.8368630409241 segundos
  • tamaño de “String” serializado: 13993

PHP nativo:

  • PHP serializado en 2.9125759601593 segundos
  • PHP sin serializar en 6.4348418712616 segundos
  • Tamaño serial de “String”: 20769

Igbinario:

  • WIN igbinario serializado en 1.6099879741669 segundos
  • GANA la biblioteca sin serializar en 4.7737920284271 segundos
  • WIN serialized “String” Tamaño: 4467

Por lo tanto, es más rápido igbinary_serialize () e igbinary_unserialize () y utiliza menos espacio en disco.

Utilicé el código fillArray (0, 3) como se indicó anteriormente, pero hice que las teclas de matriz fueran cadenas más largas.

igbinary puede almacenar los mismos tipos de datos que la serialización nativa de PHP (así que no hay problema con los objetos, etc.) y puede decirle a PHP5.3 que lo use para el manejo de la sesión si así lo desea.

Ver también http://ilia.ws/files/zendcon_2010_hidden_features.pdf – específicamente diapositivas 14/15/16

Acabo de probar serializado y json codificar y decodificar, más el tamaño que tomará la cadena almacenada.

 JSON encoded in 0.067085981369 seconds. Size (1277772) PHP serialized in 0.12110209465 seconds. Size (1955548) JSON decode in 0.22470498085 seconds PHP serialized in 0.211947917938 seconds json_encode() was roughly 80.52% faster than serialize() unserialize() was roughly 6.02% faster than json_decode() JSON string was roughly 53.04% smaller than Serialized string 

Podemos concluir que JSON codifica más rápido y resulta una cadena más pequeña, pero la desestratificación es más rápida para decodificar la cadena.

Si está almacenando en caché información que en última instancia querrá “incluir” en un momento posterior, puede intentar usar var_export . De esta forma solo tomas el hit en “serializar” y no en “deserializar”.

Aumenté la prueba para incluir el rendimiento de la deserialización. Aquí están los números que obtuve.

 Serialize JSON encoded in 2.5738489627838 seconds PHP serialized in 5.2861361503601 seconds Serialize: json_encode() was roughly 105.38% faster than serialize() Unserialize JSON decode in 10.915472984314 seconds PHP unserialized in 7.6223039627075 seconds Unserialize: unserialize() was roughly 43.20% faster than json_decode() 

Entonces, json parece ser más rápido para la encoding pero lento en la deencoding. Por lo tanto, podría depender de su aplicación y de lo que espera hacer más.

Parece que serialize es el que voy a usar por dos razones:

  • Alguien señaló que la deserialización es más rápida que json_decode y un caso de “lectura” parece más probable que un caso de “escritura”.

  • He tenido problemas con json_encode cuando tengo cadenas con caracteres UTF-8 no válidos. Cuando eso sucede, la cadena termina siendo vacía, causando la pérdida de información.

He probado esto muy a fondo en un multi-hash bastante complejo, ligeramente nested con todo tipo de datos en él (cadena, NULL, enteros), y serialize / unserialize terminó mucho más rápido que json_encode / json_decode.

La única ventaja que tiene Json en mis pruebas es que es un tamaño ‘compacto’ más pequeño.

Estos se hacen bajo PHP 5.3.3, avíseme si quiere más detalles.

Aquí están los resultados de las pruebas y luego el código para producirlos. No puedo proporcionar los datos de la prueba ya que revelaría información que no puedo dejar salir en libertad.

 JSON encoded in 2.23700618744 seconds PHP serialized in 1.3434419632 seconds JSON decoded in 4.0405561924 seconds PHP unserialized in 1.39393305779 seconds serialized size : 14549 json_encode size : 11520 serialize() was roughly 66.51% faster than json_encode() unserialize() was roughly 189.87% faster than json_decode() json_encode() string was roughly 26.29% smaller than serialize() // Time json encoding $start = microtime( true ); for($i = 0; $i < 10000; $i++) { json_encode( $test ); } $jsonTime = microtime( true ) - $start; echo "JSON encoded in $jsonTime seconds
"; // Time serialization $start = microtime( true ); for($i = 0; $i < 10000; $i++) { serialize( $test ); } $serializeTime = microtime( true ) - $start; echo "PHP serialized in $serializeTime seconds
"; // Time json decoding $test2 = json_encode( $test ); $start = microtime( true ); for($i = 0; $i < 10000; $i++) { json_decode( $test2 ); } $jsonDecodeTime = microtime( true ) - $start; echo "JSON decoded in $jsonDecodeTime seconds
"; // Time deserialization $test2 = serialize( $test ); $start = microtime( true ); for($i = 0; $i < 10000; $i++) { unserialize( $test2 ); } $unserializeTime = microtime( true ) - $start; echo "PHP unserialized in $unserializeTime seconds
"; $jsonSize = strlen(json_encode( $test )); $phpSize = strlen(serialize( $test )); echo "

serialized size : " . strlen(serialize( $test )) . "
"; echo "json_encode size : " . strlen(json_encode( $test )) . "

"; // Compare them if ( $jsonTime < $serializeTime ) { echo "json_encode() was roughly " . number_format( ($serializeTime / $jsonTime - 1 ) * 100, 2 ) . "% faster than serialize()"; } else if ( $serializeTime < $jsonTime ) { echo "serialize() was roughly " . number_format( ($jsonTime / $serializeTime - 1 ) * 100, 2 ) . "% faster than json_encode()"; } else { echo 'Unpossible!'; } echo '
'; // Compare them if ( $jsonDecodeTime < $unserializeTime ) { echo "json_decode() was roughly " . number_format( ($unserializeTime / $jsonDecodeTime - 1 ) * 100, 2 ) . "% faster than unserialize()"; } else if ( $unserializeTime < $jsonDecodeTime ) { echo "unserialize() was roughly " . number_format( ($jsonDecodeTime / $unserializeTime - 1 ) * 100, 2 ) . "% faster than json_decode()"; } else { echo 'Unpossible!'; } echo '
'; // Compare them if ( $jsonSize < $phpSize ) { echo "json_encode() string was roughly " . number_format( ($phpSize / $jsonSize - 1 ) * 100, 2 ) . "% smaller than serialize()"; } else if ( $phpSize < $jsonSize ) { echo "serialize() string was roughly " . number_format( ($jsonSize / $phpSize - 1 ) * 100, 2 ) . "% smaller than json_encode()"; } else { echo 'Unpossible!'; }

Un tema realmente agradable y después de leer las pocas respuestas, quiero compartir mis experimentos sobre el tema.

Tengo un caso de uso donde se debe consultar una tabla “grande” casi cada vez que hablo con la base de datos (no preguntes por qué, solo un hecho). El sistema de caché de la base de datos no es apropiado ya que no almacenará en caché las diferentes solicitudes, así que pensé en los sistemas de caché de php.

Intenté apcu pero no se ajustaba a las necesidades, la memoria no es lo suficientemente confiable en este caso. El siguiente paso fue almacenar en caché un archivo con serialización.

La tabla tiene 14355 entradas con 18 columnas, esas son mis pruebas y estadísticas al leer el caché serializado:

JSON:

Como todos han dicho, el principal inconveniente con json_encode / json_decode es que transforma todo en una instancia de StdClass (u Objeto). Si necesita buclearlo, transformarlo en una matriz es lo que probablemente hará, y sí, está aumentando el tiempo de transformación.

tiempo promedio: 780.2 ms; uso de memoria: 41.5MB; tamaño del archivo de caché: 3.8MB

Msgpack

@hutch menciona msgpack . Bonito sitio web. Vamos a intentarlo, ¿o sí?

tiempo promedio: 497 ms; uso de memoria: 32MB; tamaño del archivo de caché: 2.8MB

Eso es mejor, pero requiere una nueva extensión; comstackndo a veces personas temerosas …

IgBinary

@GingerDog menciona igbinario . Tenga en cuenta que he configurado igbinary.compact_strings=Off porque me preocupa más leer interpretaciones que el tamaño del archivo.

tiempo promedio: 411,4 ms; uso de memoria: 36.75MB; tamaño del archivo de caché: 3.3MB

Mejor que el paquete de mensajes. Aún así, esto también requiere comstackción.

serialize / unserialize

tiempo promedio: 477.2 ms; uso de memoria: 36.25MB; tamaño del archivo de caché: 5.9MB

Mejores actuaciones que JSON, cuanto más grande es la matriz, más json_decode es json_decode , pero ya lo sabes.

Esas extensiones externas están reduciendo el tamaño del archivo y parece genial en el papel. Los números no mienten *. ¿De qué sirve comstackr una extensión si obtiene casi los mismos resultados que tendría con una función PHP estándar?

También podemos deducir que, según sus necesidades, elegirá algo diferente a otra persona:

  • IgBinary es realmente agradable y funciona mejor que MsgPack
  • Msgpack es mejor para comprimir tus datos (ten en cuenta que no probé la opción igbinary compact.string).
  • ¿No quieres comstackr? Usa los estándares.

¡Eso es todo, otra comparación de métodos de serialización para ayudarlo a elegir el uno!

* Probado con PHPUnit 3.7.31, php 5.5.10 – solo deencoding con un hardrive estándar y un viejo CPU de doble núcleo – números promedio en 10 pruebas de casos de uso iguales, tus estadísticas pueden ser diferentes

Hice un pequeño punto de referencia también. Mis resultados fueron los mismos Pero necesito el rendimiento de deencoding. Donde me di cuenta, como algunas personas arriba también, la unserialize es más rápida que json_decode . unserialize toma aproximadamente 60-70% del tiempo de json_decode . Por lo tanto, la conclusión es bastante simple: cuando necesite rendimiento en la encoding, use json_encode , cuando necesite rendimiento durante la deencoding, utilice unserialize . Debido a que no puede fusionar las dos funciones, debe hacer una elección donde necesite más rendimiento.

Mi punto de referencia en pseudo:

  • Define array $ arr con algunas claves y valores aleatorios
  • para x <100; x ++; serialize y json_encode una array_rand de $ arr
  • para y <1000; y ++; json_decode la cadena codificada json - calc time
  • para y <1000; y ++; Desserializar la cadena serializada - tiempo de calcificación
  • repetir el resultado que fue más rápido

En avarage: unserialize ganó 96 veces más de 4 veces el json_decode. Con un promedio de aproximadamente 1.5ms sobre 2.5ms.

Antes de tomar su decisión final, tenga en cuenta que el formato JSON no es seguro para matrices asociativas; json_decode() devolverá como objetos en su lugar:

 $config = array( 'Frodo' => 'hobbit', 'Gimli' => 'dwarf', 'Gandalf' => 'wizard', ); print_r($config); print_r(json_decode(json_encode($config))); 

La salida es:

 Array ( [Frodo] => hobbit [Gimli] => dwarf [Gandalf] => wizard ) stdClass Object ( [Frodo] => hobbit [Gimli] => dwarf [Gandalf] => wizard ) 

solo un fyi: si desea serializar sus datos a algo fácil de leer y entender como JSON, pero con más compresión y mayor rendimiento, debe verificar el paquete de mensajes.

Vea los resultados aquí (lo siento por el truco al poner el código PHP en el cuadro de código JS):

http://jsfiddle.net/newms87/h3b0a0ha/embedded/result/

RESULTADOS: serialize() y unserialize() son significativamente más rápidos en PHP 5.4 en matrices de diferentes tamaños.

Hice una secuencia de comandos de prueba en datos del mundo real para comparar json_encode vs serialize y json_decode vs unserialize. La prueba se ejecutó en el sistema de almacenamiento en caché de un sitio de comercio electrónico en producción. Simplemente toma los datos que ya están en la caché y prueba los tiempos para codificar / decodificar (o serializar / deserializar) todos los datos y los pongo en una tabla fácil de ver.

Ejecuté esto en el servidor de alojamiento compartido PHP 5.4.

Los resultados fueron muy concluyentes de que para estos conjuntos de datos de grandes a pequeños serializar y deserializar fueron los claros ganadores. En particular para mi caso de uso, json_decode y unserialize son los más importantes para el sistema de caché. Unserialize fue casi un ganador omnipresente aquí. Por lo general, era de 2 a 4 veces (a veces 6 o 7 veces) tan rápido como json_decode.

Es interesante notar la diferencia en los resultados de @ peter-bailey.

Aquí está el código PHP utilizado para generar los resultados:

  basename($file), 'json_encode() Time (s)' => $json_encode_time, 'json_decode() Time (s)' => $json_decode_time, 'serialize() Time (s)' => $serialize_time, 'unserialize() Time (s)' => $unserialize_time, 'Elements' => $count, 'Memory (KB)' => $memory, 'Max Depth' => $depth, 'json_encode() Win' => ($json_encode_time > 0 && $json_encode_time < $serialize_time) ? number_format(($serialize_time / $json_encode_time - 1) * 100, 2) : '', 'serialize() Win' => ($serialize_time > 0 && $serialize_time < $json_encode_time) ? number_format(($json_encode_time / $serialize_time - 1) * 100, 2) : '', 'json_decode() Win' => ($json_decode_time > 0 && $json_decode_time < $serialize_time) ? number_format(($serialize_time / $json_decode_time - 1) * 100, 2) : '', 'unserialize() Win' => ($unserialize_time > 0 && $unserialize_time < $json_decode_time) ? number_format(($json_decode_time / $unserialize_time - 1) * 100, 2) : '', ); } $files = glob(dirname(__FILE__) . '/system/cache/*'); $data = array(); foreach ($files as $file) { if (is_file($file)) { $result = run_test($file); if ($result) { $data[] = $result; } } } uasort($data, function ($a, $b) { return $a['Memory (KB)'] < $b['Memory (KB)']; }); $fields = array_keys($data[0]); ?>  $value) { ?> 

Primero, cambié el guión para hacer más benchmarking (y también hago 1000 runs en vez de solo 1):

  

Usé esta comstackción de PHP 7:

PHP 7.0.14 (cli) (built: Jan 18 2017 19:13:23) (NTS) Copyright (c) 1997-2016 El PHP Group Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies con Zend OPcache v7.0.14, Copyright (c) 1999-2016, por Zend Technologies

Y mis resultados fueron:

serialize () (gana: 999) fue aproximadamente 10.98% más rápido que json_encode () unserialize () (wins: 987) fue aproximadamente 33.26% más rápido que json_decode () unserialize () (wins: 987) fue aproximadamente 48.35% más rápido que array json_decode ()

Entonces, claramente serialize / unserialize es el método más rápido , mientras que json_encode / decode es el más portátil.

Si considera un escenario donde lee / escribe datos serializados 10 veces o más a menudo de lo que necesita enviar o recibir desde un sistema que no es PHP, aún le conviene usar serializar / deserializar y tenerlo json_encode o json_decode antes de la serialización en términos de tiempo.

JSON es mejor si desea hacer una copia de seguridad de los datos y restaurarlos en una máquina diferente o mediante FTP.

Por ejemplo, con serializar si almacena datos en un servidor Windows, descárguelo a través de FTP y restaurelo en uno Linux ya no podría funcionar debido a la reencoding del carácter, ya que serialize almacena la longitud de las cadenas y en Unicode > La transencoding UTF-8 de un charachter de 1 byte podría tener una longitud de 2 bytes, haciendo que el algoritmo se bloquee.

THX – para este código de referencia:

Mis resultados en la matriz que uso para la configuración son como barbechos: encoding JSON en 0.0031511783599854 segundos
PHP serializado en 0.0037961006164551 segundos
json_encode() fue aproximadamente 20.47% más rápido que serialize() JSON codificado en 0.0070841312408447 segundos
PHP serializado en 0.0035839080810547 segundos
unserialize() fue aproximadamente un 97.66% más rápido que json_encode()

Entonces, pruébalo en tus propios datos.

Si resumimos lo que dice la gente aquí, json_decode / encode parece más rápido que serialize / unserialize PERO si lo haces var_dump, el tipo de objeto serializado cambia. Si por alguna razón quieres mantener el tipo, ve con serializar!

(prueba por ejemplo stdClass vs array)

serializar / deserializar:

 Array cache: array (size=2) 'a' => string '1' (length=1) 'b' => int 2 Object cache: object(stdClass)[8] public 'field1' => int 123 This cache: object(Controller\Test)[8] protected 'view' => 

json codificar / decodificar

 Array cache: object(stdClass)[7] public 'a' => string '1' (length=1) public 'b' => int 2 Object cache: object(stdClass)[8] public 'field1' => int 123 This cache: object(stdClass)[8] 

Como puede ver, json_encode / decode convierte todo a stdClass, lo cual no es tan bueno, la información del objeto se pierde … Así que decida en función de las necesidades, especialmente si no son solo las matrices …