繁体   English   中英

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

[英]serialize a large array in PHP?

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

我希望在社交网站上缓存用户的朋友列表,缓存将需要相当频繁地更新,但是几乎每个页面加载都需要读取缓存。

在单个服务器上,我假设APC会比内存缓存更好。

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

$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 (在网络服务器上,muco通常不超过32MB)
  • 您的网络服务器将承受的负载:当这些大序列化循环中的1个正在运行时,它占用了我的CPU之一; 如果您同时在同一页面上有多个用户,那么让您想象一下它会给您带来什么;-)
  • 用户的耐心^^

但是,除非您真的要序列化大数据的长数组 ,否则我不确定这有多重要...
并且您必须考虑使用该缓存的时间/ CPU负载量可能会帮助您获得;-)

尽管如此,最好的了解方法还是使用真实数据自己进行测试;-)


您可能还想看一下Xdebug在进行概要分析时可以做什么:这种情况就是其中一种有用的情况!

serialize()函数仅受可用内存的限制。

PHP没有限制。 Serialize返回序列化结构的字节流表示形式(字符串),因此您只会得到一个大字符串。

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

没有限制,但是请记住,序列化和反序列化是有代价的。

非序列化非常昂贵。

缓存数据的一种较便宜的方式是通过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()

正如上面的思想家所建议的:

你可以用

$string = json_encode($your_array_here);

并解码

$array = json_decode($your_array_here, true);

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

好吧...更多电话! (PHP 5.3.0 OSX,无操作码缓存)

我的机器上@Pascal的代码在nk = 1时以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及其包含/评估伙伴$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似乎很慢。

不,没有限制,这是:

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

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

这就是为什么应该安装safe.mode = On或安装Suhosin这样的扩展程序的原因 ,否则它将耗尽系统中的所有内存。

我认为比序列化更好的是json_encode函数。 缺点是无法区分关联的数组和对象,但是字符串结果较小,更容易被人阅读,因此也易于调试和编辑。

如果要缓存它(因此我认为是性能问题),请改用apc_add以避免将其转换为字符串+获取内存缓存的性能下降。

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

其他一些陷阱:序列化的数据在多字节和单字节字符编码之间不可移植。 PHP5类包含NUL字节,这些字节可能会导致意外的代码损坏。

用例听起来像您最好使用数据库来做到这一点,而不是仅仅依赖PHP的可用资源。 使用类似MySQL之类的东西的好处是,它是专门针对内存管理(例如存储和查找)进行设计的。

仅仅为了更新或更改一些信息而不断地序列化和反序列化数据真的没什么好玩的。

我刚刚遇到一个实例,我以为自己已达到序列化的上限。

我正在使用mysql TEXT字段将序列化的对象持久保存到数据库中。

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

我有一种情况,其中反序列化在大型序列化对象上引发异常,大小:65535(魔术数:16位全位= 65536)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM