存储PHP数组的首选方法(json_encode vs serialize)

Kyl*_*ris 590 php arrays performance serialization json

我需要在平面文件中存储多维关联数据数组以用于缓存目的.我偶尔会遇到将其转换为JSON以便在我的Web应用程序中使用的需要,但绝大多数时候我将直接在PHP中使用该数组.

将数组作为JSON或PHP序列化数组存储在此文本文件中会更有效吗?我环顾四周,似乎在最新版本的PHP(5.3)中,json_decode实际上比它更快unserialize.

我目前倾向于将数组存储为JSON,因为我觉得如果有必要,人们可以更容易地阅读它,它可以在PHP和JavaScript中轻松使用,而且从我读过的,它甚至可能是更快解码(不确定编码).

有谁知道任何陷阱?任何人都有良好的基准来展示这两种方法的性能优势?

Pet*_*ley 549

取决于您的优先事项.

如果性能是您的绝对驾驶特性,那么一定要使用最快的一个.在做出选择之前,请确保您已充分了解差异

  • serialize()您需要添加额外参数以保持UTF-8字符保持不变不同:( json_encode($array, JSON_UNESCAPED_UNICODE) 否则它将UTF-8字符转换为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';
}
Run Code Online (Sandbox Code Playgroud)

  • 这里我们差不多5年了,我再次运行测试(只是`json_encode`),它现在平均比序列化快131%.因此,对于5.3.x而不是5.3.x,必须对该功能进行一些非常好的改进.具体来说,我在CentOS 6上运行5.4.24.所以,对JSON来说! (32认同)
  • "JSON将UTF-8字符转换为unicode转义序列." 不一定是真的:我们现在有`JSON_UNESCAPED_UNICODE`. (31认同)
  • 有趣的是:如果你在[3v4l.org](http://3v4l.org/qFKvT)上运行此代码,最新的PHP7开发版本比json_encode运行序列化更快:"serialize()比json_encode()快大约76.53%" (21认同)
  • 2017年,PHP 7.1和`serialize()比json_encode()快约35.04% (17认同)
  • 在我的情况下,我们编码一次并解码很多,所以我们对json_decode和unserialize进行基准测试,结果是在0.06662392616272秒内进行了JSON编码<br> PHP在0.093269109725952秒中未反序列化<br> <br> json_decode()比unserialize()快大约39.99% (8认同)
  • 只是为了确认php7 JSON编码在0.0054829120635986秒PHP序列化0.0049090385437012秒serialize()比MacBook 2.2 GHz Intel Core i7 16GB RAM上的json_encode()大约快11.69% (6认同)
  • 2020 年,PHP 7.4:`JSON 编码时间为 0.0064802169799805 秒 PHP 序列化时间为 0.0081288814544678 秒 json_encode() 比 serialize() 大约快 25.44%` (4认同)
  • 2019,7.1.27-1`以0.036638021469116秒编码的JSON在0.027013063430786秒内序列化的PHP serialize()的速度比json_encode()快35.63%` (3认同)
  • 2021 年,PHP 8.0.3 - Intel Core i7 5.0GHz - 16gb RAM `JSON 编码需要 0.010940074920654 秒 PHP 序列化需要 0.007300853729248 秒 serialize() 比 json_encode() 快大约 49.85%` (2认同)

Gre*_*reg 236

JSON比PHP的序列化格式更简单,更快,除非:

  • 您正在存储深层嵌套数组:: json_decode()"如果JSON编码数据深于127个元素,则此函数将返回false."
  • 您正在将需要反序列化的对象存储为正确的类
  • 您正在与不支持json_decode的旧PHP版本进行交互

  • "5.3.0添加了可选深度.默认递归深度从128增加到512" (46认同)
  • 很好的答案.哈哈,127级深度似乎有点疯狂; 谢天谢地,我最多只想2-3.你有任何数据来支持json_decode/json_encode比unserialize/serialize更快的事实吗? (10认同)
  • 我将在上面的列表中再添加一个项:如果您的数据可能包含无效的UTF-8字节序列,则不应使用json_encode().它只是为这些数据返回false.试试例子:var_dump(json_encode("\ xEF\xEF")); (3认同)
  • 一般来说,它更快,这是不正确的。如果你有一个大约有 500 个条目的小数组,那么反序列化/序列化实际上比 json_decode/json_encode (PHP 5.6.19) 快 200-400% (2认同)

Tac*_*aco 59

我写了一篇关于这个主题的博文:" 缓存大型数组:JSON,序列化或var_export? ".在这篇文章中,显示序列化是小型到大型阵列的最佳选择.对于非常大的阵列(> 70MB),JSON是更好的选择.

  • 该链接不再可用. (5认同)
  • 谢谢,驼鹿,我已经更新了链接。尽管这篇文章已经有将近 6 年的历史了,对于当前的 PHP 版本来说可能不太准确。 (2认同)
  • 博客不再存在 (2认同)

Dav*_*win 52

您可能还对https://github.com/phadej/igbinary感兴趣- 它为PHP提供了不同的序列化"引擎".

我在64位平台上使用PHP 5.3.5的随机/随意"性能"数据显示:

JSON:

  • JSON编码为2.180496931076秒
  • JSON在9.8368630409241秒内解码
  • 序列化"字符串"大小:13993

原生PHP:

  • PHP序列化为2.9125759601593秒
  • PHP在6.4348418712616秒中反序列化
  • 序列化"字符串"大小:20769

Igbinary:

  • WIN igbinary序列化为1.6099879741669秒
  • WIN inigbinrary在4.7737920284271秒中反序列化
  • WIN序列化"字符串"大小: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

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
Run Code Online (Sandbox Code Playgroud)

我们可以得出结论,JSON编码速度更快,结果更小,但是反序列化对字符串的解码速度更快.

  • 我不知道为什么人们总是用如此小的数据集进行性能测试.这样做可以减少为结果添加错误的所有开销.如果人们对性能感兴趣,可能是因为他们有一个非常大的数据集,因为获得微秒一次是没有意义的. (6认同)
  • 我经常迭代许多小数据集。对于数百个小数据集,每个数据集获得 1 毫秒仍然很有趣。 (2认同)

Jor*_*nes 16

如果您要缓存最终希望"包含"的信息,则可能需要尝试使用var_export.这样你只能在"序列化"中获取命中,而不是在"反序列化"中.


Jef*_*ing 12

我增加了测试以包括反序列化性能.这是我得到的数字.

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() 
Run Code Online (Sandbox Code Playgroud)

所以json似乎更快编码,但解码速度慢.因此,它可能取决于您的应用程序以及您期望做的最多.


soy*_*uka 9

非常好的主题,在阅读了几个答案后,我想分享我在这个主题上的实验.

我有一个用例,几乎每次我与数据库交谈时都需要查询一些"巨大的"表(不要问为什么,只是一个事实).数据库缓存系统不合适,因为它不会缓存不同的请求,所以我关于php缓存系统.

我试过apcu但它不符合需要,在这种情况下内存不够可靠.下一步是使用序列化缓存到文件中.

Table有14355个条目,有18列,这些是我在读取序列化缓存时的测试和统计信息:

JSON:

正如大家所说,对json_encode/ 的主要不便之处json_decode在于它将所有内容转换为StdClass实例(或对象).如果你需要循环它,将它转换为数组是你可能会做的,是的,它增加了转换时间

平均时间:780.2毫秒; 内存使用:41.5MB; 缓存文件大小:3.8MB

Msgpack

@hutch提到了msgpack.漂亮的网站.我们试一试吧?

平均时间:497毫秒; 内存使用:32MB; 缓存文件大小:2.8MB

那更好,但需要新的扩展; 编译有时害怕人......

IgBinary

@GingerDog提到igbinary.请注意,我已经设置了igbinary.compact_strings=Off因为我更关心阅读性能而不是文件大小.

平均时间:411.4毫秒; 内存使用:36.75MB; 缓存文件大小:3.3MB

比msg包更好.不过,这个还需要编译.

serialize/unserialize

平均时间:477.2毫秒; 内存使用:36.25MB; 缓存文件大小:5.9MB

比JSON更好的性能,阵列越大,速度越慢json_decode,但你已经是新的了.

这些外部扩展正在缩小文件大小,在纸面上看起来很棒.数字不在于*.如果你得到与标准PHP函数几乎相同的结果,那么编译扩展的重点是什么?

我们还可以推断,根据您的需求,您将选择与其他人不同的东西:

  • IgBinary非常好,性能比MsgPack好
  • Msgpack更适合压缩数据(请注意,我没有尝试过igbinary compact.string选项).
  • 不想编译?使用标准.

就是这样,另一个序列化方法比较,以帮助您选择一个!

*使用PHPUnit 3.7.31,php 5.5.10进行测试 - 仅使用标准hardrive和旧的双核CPU解码 - 在10个相同的用例测试中的平均数,您的统计数据可能会有所不同


小智 8

似乎序列化是我将要使用的原因有两个:

  • 有人指出,unserialize比json_decode更快,而'read'的情况听起来比'write'的情况更可能.

  • 当我的字符串包含无效的UTF-8字符时,我遇到了json_encode问题.当发生这种情况时,字符串最终变为空,从而导致信息丢失.


小智 6

我已经在一个相当复杂,温和嵌套的多哈希中对其进行了彻底的测试,其中包含各种数据(字符串,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!';
}
Run Code Online (Sandbox Code Playgroud)


Jel*_*mer 6

我也做了一个小基准.我的结果是一样的.但我需要解码性能.在我注意到的地方,就像上面提到的一些人一样,unserialize速度比json_decode.unserialize大约需要60-70%的json_decode时间.所以结论很简单:当你需要编码json_encode性能时,在解码时需要性能时使用unserialize.因为您无法合并这两个函数,所以您需要在需要更高性能的地方进行选择.

我的伪基准:

  • 使用一些随机键和值定义数组$ arr
  • 对于x <100; X ++; serialize和json_encode $ arr的array_rand
  • 因为y <1000; ÿ++; json_decode json编码的字符串 - calc时间
  • 因为y <1000; ÿ++; 反序列化序列化字符串 - 计算时间
  • 回应更快的结果

在avarage:unserialize在json_decode的4倍上赢了96次.超过2.5毫秒的平均大约1.5毫秒.


小智 5

我知道这已经晚了,但答案已经很旧了,我认为我的基准测试可能会有所帮助,因为我刚刚在 PHP 7.4 中进行了测试

序列化/反序列化比 JSON 快得多,占用更少的内存和空间,并且在 PHP 7.4 中完全胜出,但我不确定我的测试是最有效的还是最好的,

我基本上创建了一个 PHP 文件,它返回一个我编码、序列化、然后解码和反序列化的数组。

$array = include __DIR__.'/../tests/data/dao/testfiles/testArray.php';

//JSON ENCODE
$json_encode_memory_start = memory_get_usage();
$json_encode_time_start = microtime(true);

for ($i=0; $i < 20000; $i++) { 
    $encoded = json_encode($array);
}

$json_encode_time_end = microtime(true);
$json_encode_memory_end = memory_get_usage();
$json_encode_time = $json_encode_time_end - $json_encode_time_start;
$json_encode_memory = 
$json_encode_memory_end - $json_encode_memory_start;


//SERIALIZE
$serialize_memory_start = memory_get_usage();
$serialize_time_start = microtime(true);

for ($i=0; $i < 20000; $i++) { 
    $serialized = serialize($array);
}

$serialize_time_end = microtime(true);
$serialize_memory_end = memory_get_usage();
$serialize_time = $serialize_time_end - $serialize_time_start;
$serialize_memory = $serialize_memory_end - $serialize_memory_start;


//Write to file time:
$fpc_memory_start = memory_get_usage();
$fpc_time_start = microtime(true);

for ($i=0; $i < 20000; $i++) { 
    $fpc_bytes = 
    file_put_contents(
        __DIR__.'/../tests/data/dao/testOneBigFile',
        '<?php return '.var_export($array,true).' ?>;'
    );
}

$fpc_time_end = microtime(true);
$fpc_memory_end = memory_get_usage();
$fpc_time = $fpc_time_end - $fpc_time_start;
$fpc_memory = $fpc_memory_end - $fpc_memory_start;


//JSON DECODE
$json_decode_memory_start = memory_get_usage();
$json_decode_time_start = microtime(true);

for ($i=0; $i < 20000; $i++) { 
    $decoded = json_encode($encoded);
}

$json_decode_time_end = microtime(true);
$json_decode_memory_end = memory_get_usage();
$json_decode_time = $json_decode_time_end - $json_decode_time_start;
$json_decode_memory = 
$json_decode_memory_end - $json_decode_memory_start;


//UNSERIALIZE
$unserialize_memory_start = memory_get_usage();
$unserialize_time_start = microtime(true);

for ($i=0; $i < 20000; $i++) { 
    $unserialized = unserialize($serialized);
}

$unserialize_time_end = microtime(true);
$unserialize_memory_end = memory_get_usage();
$unserialize_time = $unserialize_time_end - $unserialize_time_start;
$unserialize_memory = 
$unserialize_memory_end - $unserialize_memory_start;


//GET FROM VAR EXPORT:
$var_export_memory_start = memory_get_usage();
$var_export_time_start = microtime(true);

for ($i=0; $i < 20000; $i++) { 
    $array = include __DIR__.'/../tests/data/dao/testOneBigFile';
}

$var_export_time_end = microtime(true);
$var_export_memory_end = memory_get_usage();
$var_export_time = $var_export_time_end - $var_export_time_start;
$var_export_memory = $var_export_memory_end - $var_export_memory_start;
Run Code Online (Sandbox Code Playgroud)

结果:

Var 导出长度:11447 序列化长度:11541 Json 编码长度:11895 文件放置内容字节数:11464

Json 编码时间:1.9197590351105 序列化时间:0.160325050354 FPC 时间:6.2793469429016

Json 编码内存:12288 序列化内存:12288 FPC 内存:0

JSON 解码时间:1.7493588924408 UnSerialize 时间:0.19309520721436 Var 导出和包含:3.1974139213562

JSON 解码内存:16384 UnSerialize 内存:14360 Var 导出和包含:192