PHP - *fast* 序列化/反序列化?
Posted
技术标签:
【中文标题】PHP - *fast* 序列化/反序列化?【英文标题】:PHP - *fast* serialize/unserialize? 【发布时间】:2011-02-02 11:30:28 【问题描述】:我有一个 php 脚本,它在 a rather large CSV file (5MB+) 之上构建 binary search tree。这很好,但读取/解析/索引文件大约需要 3 秒。
现在我想我可以使用serialize()
和unserialize()
来加快这个过程。当 CSV 文件在这期间没有发生变化时,再解析也没有意义。
令我惊恐的是,我发现在我的索引对象上调用 serialize()
需要 5 秒并生成一个巨大的 (19MB) 文本文件,而 unserialize()
需要 27 秒才能将其读回。 改进看起来有点不同。 ;-)
那么 - 在 PHP 中是否有更快的机制来将大型对象图存储/恢复到磁盘或从磁盘中恢复?
(澄清一下:我正在寻找能够显着在上述 3 秒内完成反序列化工作的东西。)
【问题讨论】:
为什么不将文件中的信息存储到数据库中? 因为脚本是一个特别不想使用数据库依赖的工具的一部分。 你的索引对象是什么样的? 如果您可以完全访问 Web 服务,则可以选择编写专门用于更快 IP2country 搜索的 PHP 扩展模块。此外,监控 CSV 文件修改日期并通过命名管道提供数据的服务也可以满足您的需求。 @stereofrog:它是嵌套节点对象的树,每个对象都有一个$value
(浮点数)、一个$payload
(字符串)和$left
和$right
节点引用。没什么特别的,但它包含超过 100,000 个这样的对象。
【参考方案1】:
var_export
应该更快,因为 PHP 根本不需要处理字符串:
// export the process CSV to export.php
$php_array = read_parse_and_index_csv($csv); // takes 3 seconds
$export = var_export($php_array, true);
file_put_contents('export.php', '<?php $php_array = ' . $export . '; ?>');
然后在需要时包含 export.php:
include 'export.php';
根据您的网络服务器设置,您可能需要先chmod
export.php 才能使其可执行。
【讨论】:
我知道这是旧的,但有更好的方法,仍然使用相同的代码。而不是file_put_contents('export.php', '<?php $php_array = ' . $export . '; ?>');
,只需使用file_put_contents('export.php', '<?php return ' . $export . '; ?>');
。而不是include 'export.php';
,使用$data = include 'export.php';
。
这是一个很棒的解决方案。我总是在包含中使用 var_export 的数据,这让它更容易一些!
以 var_export 格式读取 27MB 的数据非常慢。创建 var_export 非常快。【参考方案2】:
尝试 igbinary...为我创造了奇迹:
http://pecl.php.net/package/igbinary
【讨论】:
这在我的机器上实际上速度较慢,从 0.4 秒到本机序列化,然后增加到 0.5 秒【参考方案3】:首先,您必须改变程序的工作方式。将 CSV 文件分成更小的块。这是我假设的 IP 数据存储。 .
将所有 IP 地址转换为整数或长整数。
因此,如果有查询,您可以知道要查看的部分。
有<?php ip2long() /* and */ long2ip();
函数可以做到这一点。
所以 0 到 2^32 将所有 IP 地址转换为 5000K/50K 总共 100 个较小的文件。
这种方法可以让您更快地进行序列化。
思维敏捷,代码整洁;)
【讨论】:
【参考方案4】:看来你的问题的答案是否定的。
即使您发现“二进制序列化格式”选项很可能会减慢您的预期。
因此,您可能需要考虑使用(正如其他人提到的)数据库、memcached 或在线 Web 服务。
我还想补充以下想法:
请求/响应的缓存 您的 PHP 脚本不会关闭,而是成为一个网络服务器来回答查询 或者,我敢说,更改您当前使用的数据结构和查询方法【讨论】:
你有一个丰富的数据源,提供了很多创意,我相信你会想出一些非常顺利的东西。【参考方案5】:我在这里看到两个选项
字符串序列化,最简单的形式类似于
write => implode("\x01", (array) $node);
read => explode() + $node->payload = $a[0]; $node->value = $a[1] etc
使用 pack() 进行二进制序列化
write => pack("fnna*", $node->value, $node->le, $node->ri, $node->payload);
read => $node = (object) unpack("fvalue/nre/nli/a*payload", $data);
对这两个选项进行基准测试并比较结果会很有趣。
【讨论】:
树有一个根节点。pack()
那个根节点就足够了吗,我的意思是它会打包整个图吗?
那恐怕不行。 :-\
@Tomalak 我想就一个无关的问题寻求帮助,这里是关于通过引用将字节数组传递给 COM 对象方法的堆栈溢出问题。这是***.com/questions/42189245/…当我在互联网上仔细研究时,我遇到了一些被困在同一车辙这里的人发布的相关问题bugs.php.net/bug.php?id=41286&thanks=3我依靠你的专业知识来告诉我如何去做,如果你不介意的话请。我将非常感谢您的帮助。【参考方案6】:
如果您想要速度,以低于最佳的速度写入或读取文件系统。
在大多数情况下,数据库服务器将能够比读取/写入文件的 PHP 脚本更有效地存储和检索数据。
另一种可能性是Memcached。
对象序列化并不是以其性能而闻名,而是因为它的易用性,而且它绝对不适合处理大量数据。
【讨论】:
PHP 是否没有二进制序列化格式可以将内存字节写入磁盘并简单地再次读取它们?如果 CSV 都是字符串,而索引对象实际上包含的信息比文本文件少,为什么它的序列化形式必须如此臃肿? @Tomalak:检查打包/解包 @Robert:看起来 pack 仅适用于单个值,不适用于复杂对象。 @tomalak:序列化比较慢,因为它做了很多你在对象和类方面并不总是看到的事情。它还严重依赖递归来构建嵌套数据结构的字符串表示,这也可能很慢。我认为,当您已经拥有面向表的数据 (csv) 时,关系数据库是最佳选择。【参考方案7】:SQLite 附带 PHP,您可以将其用作数据库。否则,您可以尝试使用会话,然后您不必序列化任何内容,只需保存原始 PHP 对象。
【讨论】:
我可以在 PHP 中的会话之间共享对象吗? 您无法在不同的会话之间共享它。尽管您可以通过设置自定义会话 ID 让每个人都使用相同的会话。否则,您将不得不考虑使用共享内存。 php.net/manual/en/book.shmop.php 只是一个简短的说明,以防有人偶然发现它 - NOT 使用会话来存储大型对象,更是如此 - NOT 让人们共享同一个会话。这首先破坏了使用会话的目的 - 而且,由于一次只有一个用户可以访问一个会话 ID,因此它将有效地将请求处理限制为只有 一个!无论如何,会话必须从磁盘/数据库加载! @SteveB 诚然,上下文是模糊的,但我之前在多个应用程序的共享/固定会话中使用过大型数据集。如果您正在构建非典型应用程序,非典型解决方案通常是好的解决方案。 @hiburn8 我同意这一点。如果您正在解决特定问题,那么这可能是一个不错的主意。探索每一个可用的选项是我会尊重的。根据我的经验,我可能过于偏见了。【参考方案8】:使用 JSON 之类的格式存储/加载数据怎么样?我不知道 JSON 解析器在 PHP 中的速度有多快,但在大多数语言中它通常是一种快速操作,而且它是一种轻量级格式。
http://php.net/manual/en/book.json.php
【讨论】:
是的,这适用于数据,不适用于对象图。我一直在寻找将整个对象图转储到磁盘的东西,这样我就不会因为重新创建它而受到惩罚(在解析、错误检查、对象构造方面)。 JSON 不能代表引用。它可以表示层次结构。甚至不需要循环引用,只要有parent
引用,就结束了。此外,序列化/反序列化绝对不是我的想法。
你说得对,它不能代表引用。尽管parent
引用会使对象图具有循环性,即能够到达您以前去过的地方。嗯...你可以有一个sibling
引用,它仍然是非周期性的,这使我之前的陈述错了。
我不知道 fast 或内存效率,但我有一个几乎可以工作的对象图 implementation of a JSON-serializer(和非序列化器),它确实支持循环引用。我不知道这是否是您要查找的内容 - 我的直觉是,您正在处理的数据量可能在数据库中更好。
JSON 的一个限制是 json_encode 要求字符串值采用 UTF-8 编码。以上是关于PHP - *fast* 序列化/反序列化?的主要内容,如果未能解决你的问题,请参考以下文章