Question Méthode préférée pour stocker les tableaux PHP (json_encode vs serialize)


J'ai besoin de stocker un tableau associatif multidimensionnel de données dans un fichier plat à des fins de mise en cache. Je pourrais parfois rencontrer le besoin de le convertir en JSON pour une utilisation dans mon application web mais la grande majorité du temps je vais utiliser le tableau directement en PHP.

Serait-il plus efficace de stocker le tableau en tant que JSON ou en tant que tableau sérialisé PHP dans ce fichier texte? J'ai regardé autour et il semble que dans les dernières versions de PHP (5.3), json_decode est en fait plus rapide que unserialize.

Je suis actuellement penché pour stocker le tableau en JSON car je le sens plus facile à lire par un humain si nécessaire, il peut être utilisé en PHP et JavaScript avec très peu d'effort, et d'après ce que j'ai lu, il pourrait même être plus rapide à décoder (pas sûr de l'encodage, cependant).

Est-ce que quelqu'un connaît des pièges? Quelqu'un a de bons repères pour montrer les avantages de performance de l'une ou l'autre méthode?


545
2018-04-29 20:09


origine


Réponses:


Cela dépend de vos priorités.

Si la performance est votre caractéristique de conduite absolue, alors utilisez le plus rapide. Assurez-vous de bien comprendre les différences avant de faire un choix

  • contrairement à serialize() vous devez ajouter un paramètre supplémentaire pour ne pas toucher aux caractères UTF-8: json_encode($array, JSON_UNESCAPED_UNICODE)  (sinon, il convertit les caractères UTF-8 en séquences d'échappement Unicode).
  • JSON n'aura aucun souvenir de la classe d'origine de l'objet (ils sont toujours restaurés en tant qu'instances de stdClass).
  • Vous ne pouvez pas tirer parti __sleep() et __wakeup() avec JSON
  • Par défaut, seules les propriétés publiques sont sérialisées avec JSON. (dans PHP>=5.4 vous pouvez mettre en œuvre JsonSerializable changer ce comportement).
  • JSON est plus portable

Et il y a probablement quelques autres différences auxquelles je ne peux pas penser pour le moment.

Un test de vitesse simple pour comparer les deux

<?php

ini_set('display_errors', 1);
error_reporting(E_ALL);

// Make a big, honkin test array
// You may need to adjust this depth to avoid memory limit errors
$testArray = fillArray(0, 5);

// Time json encoding
$start = microtime(true);
json_encode($testArray);
$jsonTime = microtime(true) - $start;
echo "JSON encoded in $jsonTime seconds\n";

// Time serialization
$start = microtime(true);
serialize($testArray);
$serializeTime = microtime(true) - $start;
echo "PHP serialized in $serializeTime seconds\n";

// Compare them
if ($jsonTime < $serializeTime) {
    printf("json_encode() was roughly %01.2f%% faster than serialize()\n", ($serializeTime / $jsonTime - 1) * 100);
}
else if ($serializeTime < $jsonTime ) {
    printf("serialize() was roughly %01.2f%% faster than json_encode()\n", ($jsonTime / $serializeTime - 1) * 100);
} else {
    echo "Impossible!\n";
}

function fillArray( $depth, $max ) {
    static $seed;
    if (is_null($seed)) {
        $seed = array('a', 2, 'c', 4, 'e', 6, 'g', 8, 'i', 10);
    }
    if ($depth < $max) {
        $node = array();
        foreach ($seed as $key) {
            $node[$key] = fillArray($depth + 1, $max);
        }
        return $node;
    }
    return 'empty';
}

512
2018-04-29 20:21



JSON est plus simple et plus rapide que le format de sérialisation de PHP et devrait être utilisé sauf si:

  • Vous stockez des tableaux profondément imbriqués: json_decode(): "Cette fonction retournera false si les données codées JSON sont plus profondes que 127 éléments."
  • Vous stockez des objets qui doivent être désérialisés en tant que classe correcte
  • Vous interagissez avec les anciennes versions de PHP qui ne supportent pas json_decode

222
2018-04-29 20:13



J'ai écrit un blog sur ce sujet: "Cache un grand tableau: JSON, sérialiser ou var_export?"Dans ce post, il est montré que sérialiser est le meilleur choix pour les tableaux de petite à grande taille.Pour de très grandes baies (> 70 Mo) JSON est le meilleur choix.


55
2017-07-07 14:20



Vous pourriez aussi être intéressé par https://github.com/phadej/igbinary - qui fournit un 'moteur' de sérialisation différent pour PHP.

Mes chiffres de performance aléatoires / arbitraires, en utilisant PHP 5.3.5 sur une plate-forme 64 bits:

JSON:

  • JSON encodé en 2.180496931076 secondes
  • JSON décodé en 9.8368630409241 secondes
  • sérialisé "String" taille: 13993

PHP natif:

  • PHP sérialisé en 2.9125759601593 secondes
  • PHP non sérialisé en 6.4348418712616 secondes
  • sérialisé "String" taille: 20769

Igbinary:

  • GAGNER igbinary sérialisé en 1.6099879741669 secondes
  • GAGNER igbinrary non sérialisé en 4.7737920284271 secondes
  • GAGNER sérialisé "String" Taille: 4467

Donc, c'est plus rapide pour igbinary_serialize () et igbinary_unserialize () et utilise moins d'espace disque.

J'ai utilisé le code fillArray (0, 3) comme ci-dessus, mais j'ai rendu les clés de tableau plus longues.

igbinary peut stocker les mêmes types de données que la boîte sérielle native de PHP (donc pas de problème avec les objets etc) et vous pouvez dire à PHP5.3 de l'utiliser pour la gestion de session si vous le souhaitez.

Voir également http://ilia.ws/files/zendcon_2010_hidden_features.pdf - diapositives en particulier 14/15/16


48
2018-01-27 18:53



Y vient de tester sérialisé et json coder et décoder, plus la taille qu'il prendra la chaîne stockée.

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

Nous pouvons conclure que JSON encode plus rapidement et donne une chaîne plus petite, mais la désérialisation est plus rapide pour décoder la chaîne.


23
2017-07-10 23:44



Si vous mettez en cache des informations que vous voudrez éventuellement "inclure" à un moment ultérieur, vous pouvez essayer d'utiliser var_export. De cette façon, vous ne prenez le hit que dans la "serialize" et non dans la "unserialize".


14
2018-04-29 23:04



J'ai augmenté le test pour inclure les performances de désérialisation. Voici les chiffres que j'ai.

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

Donc, JSON semble être plus rapide pour l'encodage mais lent dans le décodage. Cela dépend donc de votre application et de ce que vous attendez le plus.


11
2017-11-23 19:14



On dirait que serialize est celui que je vais utiliser pour 2 raisons:

  • Quelqu'un a fait remarquer que unserialize est plus rapide que json_decode et qu'un cas 'read' semble plus probable qu'un cas 'write'.

  • J'ai eu des problèmes avec json_encode lorsque j'ai des chaînes avec des caractères UTF-8 invalides. Quand cela arrive, la chaîne finit par être vide, entraînant une perte d'information.


8
2018-06-27 00:46



Sujet vraiment sympa et après avoir lu les quelques réponses, je veux partager mes expériences sur le sujet.

J'ai eu un cas d'utilisation où une table "énorme" doit être interrogée presque chaque fois que je parle à la base de données (ne demandez pas pourquoi, juste un fait). Le système de mise en cache de base de données n'est pas approprié car il ne mettra pas en cache les différentes requêtes, donc je pense à propos des systèmes de mise en cache php.

j'ai essayé apcu mais cela ne correspondait pas aux besoins, la mémoire n'est pas assez fiable dans ce cas. La prochaine étape consistait à mettre en cache dans un fichier avec la sérialisation.

La table a 14355 entrées avec 18 colonnes, ce sont mes tests et statistiques sur la lecture du cache sérialisé:

JSON:

Comme vous avez tous dit, le principal inconvénient avec json_encode/json_decode est qu'il transforme tout à un StdClass instance (ou objet). Si vous avez besoin de le boucler, le transformer en un tableau est ce que vous ferez probablement, et oui cela augmente le temps de transformation

temps moyen: 780,2 ms; utilisation de la mémoire: 41.5MB; Taille du fichier cache: 3.8MB

Msgpack

@hutch mentions msgpack. Joli site web. Essayons-nous?

temps moyen: 497 ms; utilisation de la mémoire: 32 Mo; taille du fichier cache: 2.8MB

C'est mieux, mais nécessite une nouvelle extension; compiler des gens parfois effrayés ...

IgBinary

@GingerDog mentions igbinary. Notez que j'ai défini le igbinary.compact_strings=Offparce que je me soucie plus de lire les performances que la taille du fichier.

temps moyen: 411,4 ms; utilisation de la mémoire: 36.75MB; Taille du fichier cache: 3.3MB

Mieux que le pack msg. Encore, celui-ci exige la compilation aussi.

serialize/unserialize

temps moyen: 477,2 ms; utilisation de la mémoire: 36,25 Mo; Taille du fichier cache: 5.9 Mo

De meilleures performances que JSON, plus le tableau est grand, plus lent json_decode est, mais vous déjà nouveau.

Ces extensions externes rétrécissent la taille du fichier et semblent excellentes sur le papier. Les chiffres ne mentent pas *. Quel est le point de compiler une extension si vous obtenez presque les mêmes résultats que vous auriez avec une fonction PHP standard?

Nous pouvons également en déduire qu'en fonction de vos besoins, vous choisirez quelque chose de différent de quelqu'un d'autre:

  • IgBinary est vraiment sympa et fonctionne mieux que MsgPack
  • Msgpack est mieux à compresser vos données (notez que je n'ai pas essayé l'igbinary option compact.string).
  • Vous ne voulez pas compiler? Utilisez des normes.

Voilà, une autre comparaison des méthodes de sérialisation pour vous aider à choisir celle-là!

* Testé avec PHPUnit 3.7.31, php 5.5.10 - décodage uniquement avec un disque dur standard et un ancien processeur dual core - nombre moyen sur 10 tests de cas d'utilisation identiques, vos statistiques peuvent être différentes


7
2018-04-17 14:35



J'ai testé cela très minutieusement sur un multi-hash assez complexe, légèrement imbriqué avec toutes sortes de données (string, NULL, entiers), et serialize / unserialize s'est avéré beaucoup plus rapide que json_encode / json_decode.

Le seul avantage que json a eu dans mes tests était sa taille plus petite.

Ceux-ci sont faits sous PHP 5.3.3, laissez-moi savoir si vous voulez plus de détails.

Voici les résultats des tests puis le code pour les produire. Je ne peux pas fournir les données de test car cela révélerait des informations que je ne peux pas laisser sortir dans la nature.

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<br>";

//  Time serialization
$start = microtime( true );
for($i = 0; $i < 10000; $i++) {
    serialize( $test );
}
$serializeTime = microtime( true ) - $start;
echo "PHP serialized in $serializeTime seconds<br>";

//  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<br>";

//  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<br>";

$jsonSize = strlen(json_encode( $test ));
$phpSize = strlen(serialize( $test ));

echo "<p>serialized size : " . strlen(serialize( $test )) . "<br>";
echo "json_encode size : " . strlen(json_encode( $test )) . "<br></p>";

//  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 '<BR>';

//  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 '<BR>';
//  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!';
}

6
2017-11-17 00:14



J'ai aussi fait un petit benchmark. Mes résultats étaient les mêmes. Mais j'ai besoin de la performance de décodage. Où j'ai remarqué, comme quelques personnes au-dessus dit aussi, unserialize est plus rapide que json_decode. unserialize prend environ 60-70% de la json_decode temps. Donc la conclusion est assez simple: Lorsque vous avez besoin de performances dans l'encodage, utilisez json_encode, lorsque vous avez besoin de performances lors du décodage, utilisez unserialize. Parce que vous ne pouvez pas fusionner les deux fonctions, vous devez faire un choix où vous avez besoin de plus de performance.

Mon benchmark en pseudo:

  • Définir array $ arr avec quelques touches et valeurs aléatoires
  • pour x <100; x ++; sérialiser et json_encode un array_rand de $ arr
  • pour y <1000; y ++; json_decode la chaîne codée json - heure de calcul
  • pour y <1000; y ++; désérialiser la chaîne sérialisée - heure de calcul
  • écho le résultat qui était plus rapide

Sur avarage: unserialize a gagné 96 fois plus de 4 fois le json_decode. Avec une moyenne d'environ 1.5ms sur 2.5ms.


5
2017-11-12 10:18