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

Множественные выходы из функции

Web

Что такое ISA Server. ISA Server простыми словами. Настройка ISA

Web

Почему команда PECL выдает длинный список ошибок

Web

Отправка электронной почты с помощью SMTP-сервера GMail со страницы PHP