Web

Предпочтительный метод хранения массивов PHP (json_encode vs serialize)

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

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

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

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

 

Ответ 1

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

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

  1. В отличие от serialize(), вам нужно добавить дополнительный параметр, чтобы сохранить символы UTF-8 нетронутыми: json_encode($array, JSON_UNESCAPED_UNICODE) (иначе он преобразует символы UTF-8 в управляющие последовательности Unicode).

  2. JSON не будет помнить о том, каким был исходный класс объекта (они всегда восстанавливаются как экземпляры stdClass).

  3. Вы не сможете использовать __sleep() и __wakeup() с JSON.

  4. По умолчанию только публичные свойства сериализуются в JSON. (в PHP>=5.4 вы можете реализовать JsonSerializable, чтобы изменить это поведение).

  5. JSON более переносимый

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

 Простой тест на скорость для сравнения:

<?php

ini_set('display_errors', 1);

error_reporting(E_ALL);

// Создайте большой тестовый массив

// Вам может понадобиться отрегулировать его глубину, чтобы избежать ошибок ограничения памяти

$testArray = fillArray(0, 5);

// Время кодирования json

$start = microtime(true);

json_encode($testArray);

$jsonTime = microtime(true) - $start;

echo "JSON encoded in $jsonTime seconds\n";

// Время сериализации

$start = microtime(true);

serialize($testArray);

$serializeTime = microtime(true) - $start;

echo "PHP serialized in $serializeTime seconds\n";

// Сравнение по времени

if ($jsonTime < $serializeTime) {

    printf("json_encode() была примерно на %01.2f%% быстрее, чем serialize()\n", ($serializeTime / $jsonTime - 1) * 100);

}

else if ($serializeTime < $jsonTime ) {

    printf("serialize() был примерно на %01.2f%% быстрее, чем json_encode()\n", ($jsonTime 

/ $serializeTime - 1) * 100);

} else {

    echo "Неопределенно!\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';

}

 

Ответ 2

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

  • Вы храните глубоко вложенные массивы:: json_decode() «Эта функция вернет false, если данные в кодировке JSON глубже 127 элементов».

  • Вы храните объекты, которые необходимо десериализовать, как правильный класс

  • Вы взаимодействуете со старыми версиями PHP, которые не поддерживают json_decode

 

Ответ 3

Вас также может заинтересовать https://github.com/phadej/igbinary, который предоставляет другой «движок» сериализации для PHP. Мои случайные/произвольные цифры «производительности» при использовании PHP 5.3.5 на 64-битной платформе показывают:

JSON:

JSON кодируется      за 2.180496931076 секунд

JSON декодируется  за 9.8368630409241 секунд

размер сериализованной «строки» : 13993

Родной PHP:

PHP сериализован за 2.9125759601593 секунды

PHP без сериализации за 6.4348418712616 секунд

размер сериализованной «String» : 20769

Бинарный код:

WIN igbinary сериализован за 1.6099879741669 секунд

WIN igbinrary unserialized in 4.7737920284271 seconds

WIN Сериализованная «Строка» Размер : 4467

Таким образом, последний вариант igbinary_serialize() и igbinary_unserialize() быстрее и использует меньше дискового пространства. Я использовал код fillArray(0, 3), как описано выше, но сделал ключи массива более длинными строками. igbinary может хранить те же типы данных, что и родной сериализатор PHP (поэтому нет проблем с объектами и т.д.), и вы можете указать PHP5.3 для использования его для работы с сессиями, если захотите.



Ответ 4

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

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

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

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

JSON:

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

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

Msgpack

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

Так лучше, но требуется дополнительное расширение.

IgBinary

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

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

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

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 тестам с одним и тем же сценарием использования, ваша статистика может отличаться.

 

Схожие статьи

Web

Как мне получить имя файла из полного пути с помощью PHP

Текст в две колонки HTML: как это сделать и какие теги использовать?
Web

Текст в две колонки HTML: как это сделать и какие теги использовать?

Web

Почему форма с enctype возвращает нулевой файл в запросе

Web

Как подключиться к нескольким базам данных MySQL на одной веб-странице