在PHP中序列化一个大型数组?

时间:2009-08-10 20:12:22

标签: php serialization

我很好奇,PHP中的序列化是否有大小限制。是否可以使用5,000个键和值序列化数组,以便将其存储到缓存中?

我希望在社交网站上缓存用户朋友列表,缓存需要经常更新,但几乎每个页面加载都需要读取。

在单个服务器设置上,我假设APC会比memcache更好。

13 个答案:

答案 0 :(得分:27)

正如其他几个人已经回答的那样,只是为了好玩,这是一个非常快速的基准(我敢称之为吗?);请考虑以下代码:

$num = 1;

$list = array_fill(0, 5000, str_repeat('1234567890', $num));

$before = microtime(true);
for ($i=0 ; $i<10000 ; $i++) {
    $str = serialize($list);
}
$after = microtime(true);

var_dump($after-$before);
var_dump(memory_get_peak_usage());

我在PHP 5.2.6(与Ubuntu jaunty捆绑的那个)上运行它。
而且,是的,只有价值观;没钥匙;而且值非常简单:没有对象,没有子数组,只有字符串。

对于$num = 1,您会得到:

float(11.8147978783)
int(1702688)

对于$num = 10,您会得到:

float(13.1230671406)
int(2612104)

而且,对于$num = 100,你得到:

float(63.2925770283)
int(11621760)

所以,似乎数组的每个元素越大,它需要的时间就越长(看起来很公平,实际上)。但是,对于100倍大的元素,你不需要花费100倍的时间......


现在,使用50000个元素的数组,而不是5000,这意味着这部分代码已更改:

$list = array_fill(0, 50000, str_repeat('1234567890', $num));

使用$num = 1,您将获得:

float(158.236332178)
int(15750752)

考虑到1花费的时间,我不会为$ num = 10或$ num = 100运行此...


是的,当然,在真实的情况下,你不会这样做10000次;所以让我们只尝试for循环的10次迭代。

$num = 1

float(0.206310987473)
int(15750752)

$num = 10

float(0.272629022598)
int(24849832)

对于$num = 100

float(0.895547151566)
int(114949792)

是的,这几乎是1秒 - 并且使用了相当多的内存^^
(不,这不是生产服务器:我在这台开发机器上有一个相当高的memory_limit ^^)


所以,最后,要比那些数字稍微短一点 - 是的,你可以让数字说出你想要的任何数字 - 我不会说有一个“限制”就像在PHP中的“硬编码”一样,但你最终会遇到其中一个:

  • max_execution_time(通常,在网络服务器上,它不会超过30秒)
  • memory_limit(在网络服务器上,通常不超过32MB)
  • 你的网络服务器将拥有的负载:当其中一个大型序列化循环正在运行时,我占用了1个CPU;如果你同时在同一页面上有很多用户,我会让你想象它会给出什么; - )
  • 您的用户的耐心^^

但是,除非你真的在序列化长数据大数据,否则我不确定它会那么重要......
您必须考虑使用该缓存可以帮助您获得的时间/ CPU负载量; - )

然而,最好的方法是用自己的真实数据进行测试; - )


你可能还想看一下Xdebug profiling可以做些什么:这种情况是有用的一种情况!

答案 1 :(得分:7)

serialize()功能仅受可用内存的限制。

答案 2 :(得分:5)

PHP没有强制执行限制。 Serialize返回序列化结构的字节流表示(字符串),因此您只需获得一个大字符串。

答案 3 :(得分:4)

唯一可行的限制是你的可用内存,因为序列化涉及在内存中创建一个字符串。

答案 4 :(得分:3)

没有限制,但请记住序列化和反序列化有成本。

反序列化成本极高。

缓存数据的成本较低的方法是通过var_export()本身(因为PHP 5.1.0,它适用于对象):

$largeArray = array(1,2,3,'hello'=>'world',4);

file_put_contents('cache.php', "<?php\nreturn ".
                                var_export($largeArray, true).
                                ';');

然后,您可以通过执行以下操作来简单地检索数组:

$largeArray = include('cache.php');

资源通常无法缓存。

不幸的是,如果您的数组中有循环引用,则需要使用serialize()

答案 5 :(得分:3)

正如以上思想家所建议的那样:

您可以使用

$string = json_encode($your_array_here);

并解码

$array = json_decode($your_array_here, true);

这将返回一个数组。即使编码数组是多级的,它也能正常工作。

答案 6 :(得分:2)

好的...更多数字! (PHP 5.3.0 OSX,没有操作码缓存)

我的机器上的@Pascal代码,在10k时n = 1,产生:

float(18.884856939316)
int(1075900)

我将unserialize()添加到上面。

$num = 1;

$list = array_fill(0, 5000, str_repeat('1234567890', $num));

$before = microtime(true);
for ($i=0 ; $i<10000 ; $i++) {
    $str = serialize($list);
    $list = unserialize($str);
}
$after = microtime(true);

var_dump($after-$before);
var_dump(memory_get_peak_usage());

生成

float(50.204112052917)
int(1606768) 

我假设额外的600k左右是序列化字符串。

我很好奇var_export及其include / eval伙伴$str = var_export($list, true);而不是原始产品中的serialize()

float(57.064643859863)
int(1066440)

所以只需要少一点内存(至少对于这个简单的例子),但已经有更多的时间了。

eval('$list = '.$str.';');中添加而不是在上面生成

中的反序列化
float(126.62566018105)
int(2944144)

表示在执行eval时某处可能存在内存泄漏: - /。

再说一次,这些不是很好的基准测试(我真的应该通过将字符串放在本地var或其他东西中来隔离eval / unserialize,但我很懒)但它们显示了相关的趋势。 var_export似乎很慢。

答案 7 :(得分:1)

不,没有限制,这是:

set_time_limit(0);
ini_set('memory_limit ', -1);

unserialize('s:2000000000:"a";');

为什么你应该安装safe.mode = On或像Suhosin安装的扩展程序,否则会占用你系统中的所有内存。

答案 8 :(得分:1)

我认为比序列化更好json_encode函数。它有一个缺点,关联数组和对象没有区别,但字符串结果更小,更容易被人阅读,所以也可以调试和编辑。

答案 9 :(得分:1)

如果你想缓存它(因此我假设性能是问题),请改用apc_add以避免在内存中将其转换为字符串+增益缓存时的性能损失。

如上所述,唯一的尺寸限制是可用内存。

其他一些问题: 序列化数据在多字节和单字节字符编码之间不可移植。 PHP5类包含NUL字节,这些字节可能会导致破坏不期望它们的代码。

答案 10 :(得分:1)

您的用例听起来好像您最好使用数据库来做到这一点,而不是仅仅依靠PHP的可用资源。使用像MySQL这样的东西的优势在于它专门设计了内存管理,用于存储和查找等。

为了更新或更改一些信息,不断地序列化和反序列化数据真的很有趣。

答案 11 :(得分:0)

我有一个案例,其中反序列化会在大型序列化对象上抛出异常,大小:65535(幻数:16位满位= 65536)

答案 12 :(得分:0)

我刚刚遇到一个实例,我认为我达到了序列化的上限。

我使用mysql TEXT字段将序列化对象持久化到数据库。

单字节字符的可用字符数限制为65,535,因此我可以序列化比PHP更大的对象。由于它们被TEXT字段的限制截断,因此无法对它们进行反序列化。