Предпочтительный метод для хранения массивов PHP (json_encode vs serialize) - PullRequest
579 голосов
/ 30 апреля 2009

Мне нужно хранить многомерный ассоциативный массив данных в плоский файл для целей кэширования. Иногда я могу столкнуться с необходимостью преобразования его в JSON для использования в моем веб-приложении, но в большинстве случаев я буду использовать массив непосредственно в PHP.

Было бы более эффективно хранить массив в виде JSON или сериализованного массива PHP в этом текстовом файле? Я огляделся и кажется, что в новейших версиях PHP (5.3) json_decode на самом деле быстрее, чем unserialize.

В настоящее время я склоняюсь к сохранению массива в формате JSON, так как мне кажется, что его легче читать человеку, если это необходимо, его можно использовать как на PHP, так и на JavaScript с минимальными усилиями, и, как я прочитал, может даже быстрее декодироваться (хотя не уверен насчет кодировки).

Кто-нибудь знает какие-нибудь подводные камни? У кого-нибудь есть хорошие тесты производительности, демонстрирующие преимущества любого из этих методов?

Ответы [ 19 ]

541 голосов
/ 30 апреля 2009

Зависит от ваших приоритетов.

Если производительность является вашей абсолютной характеристикой вождения, то обязательно используйте самую быструю. Просто убедитесь, что у вас есть полное понимание различий, прежде чем сделать выбор

  • В отличие от serialize() вам необходимо добавить дополнительный параметр, чтобы сохранить символы UTF-8 без изменений: json_encode($array, JSON_UNESCAPED_UNICODE) (в противном случае он преобразует символы UTF-8 в escape-последовательности Unicode).
  • JSON не будет иметь памяти о том, каким был исходный класс объекта (они всегда восстанавливаются как экземпляры stdClass).
  • Вы не можете использовать __sleep() и __wakeup() с JSON
  • По умолчанию только открытые свойства сериализуются с JSON. (в PHP>=5.4 вы можете реализовать JsonSerializable , чтобы изменить это поведение).
  • JSON более переносим

И, наверное, есть еще несколько отличий, о которых я сейчас не думаю.

Простой тест скорости для сравнения двух

<?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';
}
233 голосов
/ 30 апреля 2009

JSON проще и быстрее, чем формат сериализации PHP, и его следует использовать , если :

  • Вы храните глубоко вложенные массивы: json_decode(): "Эта функция вернет false, если данные в кодировке JSON глубже 127 элементов."
  • Вы храните объекты, которые должны быть не сериализованы, как правильный класс
  • Вы взаимодействуете со старыми версиями PHP, которые не поддерживают json_decode
60 голосов
/ 07 июля 2009

Я написал статью на эту тему: « Кэшировать большой массив: JSON, serialize или var_export? ». В этом посте показано, что сериализация - лучший выбор для маленьких и больших массивов. Для очень больших массивов (> 70 МБ) JSON - лучший выбор.

52 голосов
/ 27 января 2011

Вас также может заинтересовать https://github.com/phadej/igbinary - который предоставляет другой механизм сериализации для PHP.

Мои случайные / произвольные показатели «производительности» с использованием PHP 5.3.5 на 64-битной платформе показывают:

JSON:

  • JSON, закодированный за 2,180496931076 секунд
  • JSON, декодированный за 9,8368630409241 секунд
  • Сериализованный размер "String": 13993

Родной PHP:

  • PHP сериализован за 2,9125759601593 секунд
  • PHP не сериализован за 6,4348418712616 секунд
  • сериализованный размер "String": 20769

Игбинар:

  • WIN igbinary, сериализованный за 1.6099879741669 секунд
  • WIN igbinrary несериализовано в 4,7737920284271 секунд
  • WIN сериализованный "String" Размер: 4467

Итак, igbinary_serialize () и igbinary_unserialize () быстрее, и они занимают меньше места на диске.

Я использовал код fillArray (0, 3), как указано выше, но сделал ключи массива более длинными строками.

igbinary может хранить те же типы данных, что и собственная сериализация PHP (поэтому нет проблем с объектами и т. Д.), И вы можете указать PHP5.3 использовать его для обработки сеанса, если вы того пожелаете.

См. Также http://ilia.ws/files/zendcon_2010_hidden_features.pdf - специально для слайдов 14/15/16

25 голосов
/ 11 июля 2010

Y только что протестировал сериализованный код и json кодирование и декодирование, а также размер, в котором будет храниться строка.

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

Мы можем сделать вывод, что JSON кодирует быстрее и приводит к получению строки меньшего размера, но десериализация быстрее для декодирования строки.

16 голосов
/ 30 апреля 2009

Если вы кэшируете информацию, которую в конечном итоге захотите «включить» на более позднем этапе, вы можете попробовать использовать var_export . Таким образом, вы получаете удар только в «сериализации», а не в «сериализации».

12 голосов
/ 23 ноября 2009

Я добавил в тест тест производительности десериализации. Вот цифры, которые я получил.

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

Таким образом, json кажется более быстрым для кодирования, но медленным в декодировании. Так что это может зависеть от вашей заявки и того, что вы ожидаете сделать больше всего.

9 голосов
/ 17 апреля 2014

Действительно хорошая тема, и после прочтения нескольких ответов я хочу поделиться своими экспериментами по этому вопросу.

У меня есть сценарий использования, когда какую-то "огромную" таблицу нужно запрашивать почти каждый раз, когда я общаюсь с базой данных (не спрашивайте почему, просто факт). Система кэширования базы данных не подходит, поскольку она не будет кэшировать различные запросы, поэтому я думаю о системах кэширования php.

Я пытался apcu, но это не соответствовало потребностям, память в этом случае недостаточно надежна. Следующим шагом было кеширование в файл с сериализацией.

Таблица содержит 14355 записей с 18 столбцами, это мои тесты и статистика по чтению сериализованного кэша:

JSON:

Как вы все сказали, главное неудобство с json_encode / json_decode заключается в том, что он преобразует все в экземпляр StdClass (или Объект). Если вам нужно его зациклить, вы, вероятно, будете преобразовывать его в массив, и да, это увеличивает время преобразования

среднее время: 780,2 мс; использование памяти: 41,5 МБ; размер файла кэша: 3,8 МБ

Msgpack

@ hutch упоминает msgpack . Симпатичный сайт. Давайте попробуем?

среднее время: 497 мс; использование памяти: 32 МБ; размер файла кэша: 2,8 МБ

Это лучше, но требует нового расширения; составление иногда испуганных людей ...

IgBinary

@ GingerDog упоминает igbinary . Обратите внимание, что я установил igbinary.compact_strings=Off, потому что меня больше интересует производительность чтения, чем размер файла.

среднее время: 411,4 мс; использование памяти: 36,75 МБ; размер файла кэша: 3,3 МБ

Лучше, чем MSG Pack. Тем не менее, этот тоже требует компиляции.

serialize / unserialize

среднее время: 477,2 мс; использование памяти: 36,25 МБ; размер файла кэша: 5,9 МБ

Лучшая производительность, чем у JSON, чем больше массив, тем медленнее json_decode, но вы уже новичок в этом.

Эти внешние расширения сужают размер файла и кажутся великолепными на бумаге. Числа не лгут *. Какой смысл компилировать расширение, если вы получаете почти те же результаты, что и при использовании стандартной функции PHP?

Мы также можем сделать вывод, что в зависимости от ваших потребностей вы выберете что-то другое, чем кто-либо другой:

  • IgBinary действительно хорош и работает лучше, чем MsgPack
  • Msgpack лучше сжимает ваши данные (обратите внимание, что я не пробовал igbinary опция compact.string).
  • Не хотите компилировать? Используйте стандарты.

Вот и все, еще одно сравнение методов сериализации, чтобы помочь вам выбрать один!

* Протестировано с PHPUnit 3.7.31, php 5.5.10 - только декодирование со стандартным жестким диском и старым двухъядерным ЦП - средние числа в 10 тестах с одинаковыми вариантами использования, ваша статистика может отличаться

8 голосов
/ 27 июня 2010

Похоже, я буду использовать сериализацию по двум причинам:

  • Кто-то указал, что десериализация выполняется быстрее, чем json_decode, и случай «чтения» звучит более вероятно, чем случай «записи».

  • У меня были проблемы с json_encode при наличии строк с недопустимыми символами UTF-8. Когда это происходит, строка оказывается пустой, что приводит к потере информации.

6 голосов
/ 17 ноября 2010

Я очень тщательно протестировал это на довольно сложном, слегка вложенном мультихэше со всеми видами данных в нем (строка, NULL, целые числа), и сериализация / десериализация закончилась намного быстрее, чем json_encode / json_decode. *

Единственное преимущество json в моих тестах заключалось в том, что он меньше по размеру "упакован".

Это сделано в PHP 5.3.3, дайте мне знать, если вы хотите получить более подробную информацию.

Вот результаты тестов, а затем код для их создания. Я не могу предоставить тестовые данные, поскольку они раскрывают информацию, которую я не могу выпустить в дикую природу.

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!';
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...