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

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

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

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

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

Ответы [ 19 ]

6 голосов
/ 12 ноября 2012

Я также сделал небольшой тест. Мои результаты были одинаковыми. Но мне нужно производительность декодирования. Там, где я заметил, как говорили несколько человек выше, unserialize быстрее json_decode. unserialize занимает примерно 60-70% времени json_decode. Итак, вывод довольно прост: Когда вам нужна производительность при кодировании, используйте json_encode, когда вам нужна производительность при декодировании, используйте unserialize. Поскольку вы не можете объединить две функции, вы должны сделать выбор там, где вам нужно больше производительности.

Мой тест в псевдо:

  • Определить массив $ arr с несколькими случайными ключами и значениями
  • для х <100; х ++; сериализация и json_encode массива и $ arr </li>
  • для y <1000; у ++; json_decode строка в кодировке json - время расчета </li>
  • для y <1000; у ++; десериализовать сериализованную строку - время вычисления </li>
  • повторить результат, который был быстрее

В среднем: unserialize выиграл в 96 раз больше, чем json_decode. С средним значением 1,5 мс над 2,5 мс

3 голосов
/ 21 октября 2009

Прежде чем принять окончательное решение, имейте в виду, что формат JSON небезопасен для ассоциативных массивов - json_decode() вместо этого возвратит их в виде объектов:

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

Вывод:

Array
(
    [Frodo] => hobbit
    [Gimli] => dwarf
    [Gandalf] => wizard
)
stdClass Object
(
    [Frodo] => hobbit
    [Gimli] => dwarf
    [Gandalf] => wizard
)
2 голосов
/ 07 сентября 2014

Проверьте результаты здесь (извините за взлом, поместив код PHP в поле кода JS):

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

РЕЗУЛЬТАТЫ: serialize() и unserialize() значительно быстрее в PHP 5.4 для массивов различного размера.

Я создал тестовый сценарий для данных реального мира для сравнения json_encode с serialize и json_decode с unserialize. Тест проводился на системе кеширования на сайте электронной коммерции. Он просто берет данные, уже находящиеся в кэше, и проверяет время для кодирования / декодирования (или сериализации / десериализации) всех данных, и я помещаю их в легко видимую таблицу.

Я запустил это на сервере общего хостинга PHP 5.4.

Результаты были очень убедительными, что для этих больших и малых наборов данных сериализация и десериализация были явными победителями. В частности, для моего случая использования json_decode и unserialize являются наиболее важными для системы кэширования. Unserialize был почти повсеместным победителем здесь. Обычно это было в 2-4 раза (иногда в 6 или 7 раз) быстрее, чем json_decode.

Интересно отметить разницу в результатах от @ peter-bailey.

Вот код PHP, используемый для генерации результатов:

<?php

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

function _count_depth($array)
{
    $count     = 0;
    $max_depth = 0;
    foreach ($array as $a) {
        if (is_array($a)) {
            list($cnt, $depth) = _count_depth($a);
            $count += $cnt;
            $max_depth = max($max_depth, $depth);
        } else {
            $count++;
        }
    }

    return array(
        $count,
        $max_depth + 1,
    );
}

function run_test($file)
{
    $memory     = memory_get_usage();
    $test_array = unserialize(file_get_contents($file));
    $memory     = round((memory_get_usage() - $memory) / 1024, 2);

    if (empty($test_array) || !is_array($test_array)) {
        return;
    }

    list($count, $depth) = _count_depth($test_array);

    //JSON encode test
    $start            = microtime(true);
    $json_encoded     = json_encode($test_array);
    $json_encode_time = microtime(true) - $start;

    //JSON decode test
    $start = microtime(true);
    json_decode($json_encoded);
    $json_decode_time = microtime(true) - $start;

    //serialize test
    $start          = microtime(true);
    $serialized     = serialize($test_array);
    $serialize_time = microtime(true) - $start;

    //unserialize test
    $start = microtime(true);
    unserialize($serialized);
    $unserialize_time = microtime(true) - $start;

    return array(
        'Name'                   => 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]);
?>

<table>
    <thead>
    <tr>
        <?php foreach ($fields as $f) { ?>
            <td style="text-align: center; border:1px solid black;padding: 4px 8px;font-weight:bold;font-size:1.1em"><?= $f; ?></td>
        <?php } ?>
    </tr>
    </thead>

    <tbody>
    <?php foreach ($data as $d) { ?>
        <tr>
            <?php foreach ($d as $key => $value) { ?>
                <?php $is_win = strpos($key, 'Win'); ?>
                <?php $color = ($is_win && $value) ? 'color: green;font-weight:bold;' : ''; ?>
                <td style="text-align: center; vertical-align: middle; padding: 3px 6px; border: 1px solid gray; <?= $color; ?>"><?= $value . (($is_win && $value) ? '%' : ''); ?></td>
            <?php } ?>
        </tr>
    <?php } ?>
    </tbody>
</table>
1 голос
/ 05 февраля 2017

Во-первых, я изменил скрипт, чтобы сделать еще несколько тестов (а также выполнить 1000 прогонов вместо 1):

<?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);

$totalJsonTime = 0;
$totalSerializeTime = 0;
$totalJsonWins = 0;

for ($i = 0; $i < 1000; $i++) {
    // Time json encoding
    $start = microtime(true);
    $json = json_encode($testArray);
    $jsonTime = microtime(true) - $start;
    $totalJsonTime += $jsonTime;

    // Time serialization
    $start = microtime(true);
    $serial = serialize($testArray);
    $serializeTime = microtime(true) - $start;
    $totalSerializeTime += $serializeTime;

    if ($jsonTime < $serializeTime) {
        $totalJsonWins++;
    }
}

$totalSerializeWins = 1000 - $totalJsonWins;

// Compare them
if ($totalJsonTime < $totalSerializeTime) {
    printf("json_encode() (wins: $totalJsonWins) was roughly %01.2f%% faster than serialize()\n", ($totalSerializeTime / $totalJsonTime - 1) * 100);
} else {
    printf("serialize() (wins: $totalSerializeWins) was roughly %01.2f%% faster than json_encode()\n", ($totalJsonTime / $totalSerializeTime - 1) * 100);
}

$totalJsonTime = 0;
$totalJson2Time = 0;
$totalSerializeTime = 0;
$totalJsonWins = 0;

for ($i = 0; $i < 1000; $i++) {
    // Time json decoding
    $start = microtime(true);
    $orig = json_decode($json, true);
    $jsonTime = microtime(true) - $start;
    $totalJsonTime += $jsonTime;

    $start = microtime(true);
    $origObj = json_decode($json);
    $jsonTime2 = microtime(true) - $start;
    $totalJson2Time += $jsonTime2;

    // Time serialization
    $start = microtime(true);
    $unserial = unserialize($serial);
    $serializeTime = microtime(true) - $start;
    $totalSerializeTime += $serializeTime;

    if ($jsonTime < $serializeTime) {
        $totalJsonWins++;
    }
}

$totalSerializeWins = 1000 - $totalJsonWins;


// Compare them
if ($totalJsonTime < $totalSerializeTime) {
    printf("json_decode() was roughly %01.2f%% faster than unserialize()\n", ($totalSerializeTime / $totalJsonTime - 1) * 100);
} else {
    printf("unserialize() (wins: $totalSerializeWins) was roughly %01.2f%% faster than json_decode()\n", ($totalJsonTime / $totalSerializeTime - 1) * 100);
}

// Compare them
if ($totalJson2Time < $totalSerializeTime) {
    printf("json_decode() was roughly %01.2f%% faster than unserialize()\n", ($totalSerializeTime / $totalJson2Time - 1) * 100);
} else {
    printf("unserialize() (wins: $totalSerializeWins) was roughly %01.2f%% faster than array json_decode()\n", ($totalJson2Time / $totalSerializeTime - 1) * 100);
}

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';
}

Я использовал эту сборку PHP 7:

PHP 7.0.14 (cli) (сборка: 18 января 2017 г., 19:13:23) (NTS). Copyright (c) 1997-2016 PHP Group Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies с Zend OPcache v7.0.14, Copyright (c) 1999-2016, Zend Technologies

И мои результаты были:

serialize () (побед: 999) был примерно на 10,98% быстрее, чем json_encode () unserialize () (побед: 987) был примерно на 33,26% быстрее, чем json_decode () unserialize () (побед: 987) был примерно на 48,35% быстрее, чем массив json_decode ()

То есть ясно , сериализация / десериализация - это самый быстрый метод, тогда как json_encode / decode - это самый переносимый.

Если вы рассматриваете сценарий, в котором вы читаете / записываете сериализованные данные в 10 раз или чаще, чем вам нужно отправлять или получать из системы, отличной от PHP, вам все равно лучше использовать serialize / unserialize и иметь json_encode или json_decode до сериализации с точки зрения времени.

1 голос
/ 12 апреля 2013

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

0 голосов
/ 19 июня 2018

Я бы предложил вам использовать Super Cache, который представляет собой механизм файлового кэша, который не будет использовать json_encode или serialize. Он прост в использовании и действительно быстр по сравнению с другим механизмом PHP Cache.

https://packagist.org/packages/smart-php/super-cache

Пример:

<?php
require __DIR__.'/vendor/autoload.php';
use SuperCache\SuperCache as sCache;

//Saving cache value with a key
// sCache::cache('<key>')->set('<value>');
sCache::cache('myKey')->set('Key_value');

//Retrieving cache value with a key
echo sCache::cache('myKey')->get();
?>
0 голосов
/ 30 июля 2017

Если подытожить, что люди здесь говорят, json_decode / encode кажется быстрее, чем сериализация / десериализация, НО Если вы сделаете var_dump, тип сериализованного объекта будет изменен. Если по какой-то причине вы хотите сохранить тип, переходите к serialize!

(попробуйте, например, stdClass vs array)

сериализации / десериализации:

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 кодирование / декодирование

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]

Как вы можете видеть, json_encode / decode преобразует все в stdClass, что не очень хорошо, информация об объекте теряется ... Так что решайте исходя из потребностей, особенно если это не только массивы ...

0 голосов
/ 26 ноября 2011

JSON лучше, если вы хотите сделать резервную копию данных и восстановить их на другом компьютере или через FTP.

Например, при использовании serialize, если вы храните данные на сервере Windows, загружаете их по FTP и восстанавливаете на Linux, который больше не может работать из-за перекодировки charachter, поскольку serialize хранит длину строк и в транскодировании Unicode> UTF-8 некоторые 1-байтовые символы могут стать длиной 2 байта, что приводит к сбою алгоритма.

0 голосов
/ 28 июля 2010

THX - для этого кода теста:

Мои результаты по массиву, который я использую для конфигурации, выглядят следующим образом: JSON закодирован в 0,0031511783599854 секунд
PHP сериализован за 0,0037961006164551 секунд
json_encode() был примерно на 20,47% быстрее, чем serialize() JSON, закодированный в 0,0070841312408447 секунд
PHP сериализован за 0.0035839080810547 секунд
unserialize() был примерно на 97,66% быстрее, чем json_encode()

Итак - проверьте это на своих данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...