我需要在平面文件中存储多维数据关联数组,以便进行缓存。我可能偶尔会遇到需要将其转换为JSON以在我的web应用程序中使用,但绝大多数情况下,我将直接在PHP中使用数组。

在这个文本文件中将数组存储为JSON还是PHP序列化数组更有效?我查看了一下,似乎在最新版本的PHP(5.3)中,json_decode实际上比反序列化更快。

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

有人知道有什么陷阱吗?有人有好的基准来显示这两种方法的性能优势吗?


当前回答

真是个不错的话题,在看了几个答案后,我想分享我在这个问题上的实验。

我有一个用例,几乎每次与数据库对话时都需要查询一些“巨大的”表(不要问为什么,只是一个事实)。数据库缓存系统是不合适的,因为它不会缓存不同的请求,所以我想到了php缓存系统。

我尝试了apcu,但它不符合需求,内存在这种情况下不够可靠。下一步是通过序列化将缓存到文件中。

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

JSON:

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

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

Msgpack

@hutch提到了msgpack。漂亮的网站。让我们试一试,好吗?

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

这样好多了,但需要一个新的扩展;编译有时害怕的人…

IgBinary

@GingerDog提到了igbinary。注意,我设置了igbinary.compact_strings= off,因为我更关心读取性能而不是文件大小。

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

比味精包装好。不过,这个也需要编译。

系列化/非系列化

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

比JSON更好的性能,数组越大,json_decode就越慢,但你已经知道了。

这些外部扩展缩小了文件大小,在纸上看起来很棒。数字不会说谎。如果您得到的结果与使用标准PHP函数得到的结果几乎相同,那么编译扩展还有什么意义呢?

我们还可以推断出,根据你的需求,你会选择与别人不同的东西:

IgBinary真的很好,比MsgPack执行得更好 Msgpack更擅长压缩数据(注意,我没有尝试igbinary 紧凑。字符串选项)。 不想编译?使用标准。

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

*使用PHPUnit 3.7.31测试,php 5.5.10 -仅使用标准硬盘和旧双核CPU解码- 10个相同用例测试的平均数字,您的统计数据可能不同

其他回答

我已经在一个相当复杂、嵌套简单、包含各种数据(字符串、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!';
}

您可能还会对https://github.com/phadej/igbinary感兴趣——它为PHP提供了一个不同的序列化“引擎”。

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

JSON:

JSON编码在2.180496931076秒 JSON解码在9.8368630409241秒 serialized "String" size: 13993

原生PHP:

PHP在2.9125759601593秒内序列化 PHP在6.4348418712616秒内反序列化 序列化的“字符串”大小:20769

Igbinary:

winigbinary在1.6099879741669秒内序列化 winigbinary在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

似乎serialize是我要使用的一个,有两个原因:

有人指出,unserialize比json_decode更快,而且'read' case听起来比'write' case更有可能。 当使用无效UTF-8字符的字符串时,我遇到了json_encode的问题。当这种情况发生时,字符串最终为空,导致信息丢失。

我建议您使用超级缓存,这是一种不使用json_encode或序列化的文件缓存机制。与其他PHP缓存机制相比,它使用简单,速度非常快。

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

Ex:

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

在你做出最终决定之前,请注意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
)