PHP - *快速*序列化/反序列化?

17
我有一个PHP脚本,可以在相当大的CSV文件(5MB+)上构建二叉搜索树。这很好,但读取/解析/索引该文件需要大约3秒钟。
现在我想使用serialize()unserialize()来加快进程。当CSV文件在此期间未更改时,解析它没有意义。
令我震惊的是,调用serialize()我的索引对象需要5秒钟,并生成一个巨大的(19MB)文本文件,而unserialize()需要难以忍受的27秒才能读取它。改进看起来有些不同。 ;-)
那么,在PHP中存储/恢复大型对象图形到/从磁盘的机制是否更快呢?
(澄清一下:我正在寻找的是某种需要显着少于上述3秒钟来完成反序列化工作的东西。)

为什么不将文件中的信息存储到数据库中呢? - RJD22
因为该脚本是工具的一部分,该工具明确不想使用数据库依赖性。 - Tomalak
你的索引对象长什么样? - user187291
@stereofrog: 它是一个嵌套节点对象的树,每个节点都有一个 $value(浮点数),一个 $payload(字符串)以及 $left$right 节点引用。没有什么花哨的东西,但它包含了超过 100,000 个这样的对象。 - Tomalak
@Robert:我正在寻找一个自包含的、仅限于PHP的解决方案,它不会对平台或其他已安装软件(如DB服务器)产生任何影响。 - Tomalak
显示剩余3条评论
8个回答

13

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';

根据您的Web服务器设置,您可能需要先chmod export.php,使其可执行。


8
虽然这是旧的方法,但有更好的方式,仍然使用相同的代码。不要再使用file_put_contents('export.php', '<?php $php_array = ' . $export . '; ?>');,而是改为使用file_put_contents('export.php', '<?php return ' . $export . '; ?>');。而且,不要再使用include 'export.php';,而是使用$data = include 'export.php'; - Ismael Miguel
这是一个很棒的解决方案。我总是在包含文件中使用 var_export,这使得它变得更加容易! - Gfra54
读取27MB的var_export格式数据非常缓慢。创建var_export非常快。 - William Desportes

6

在我的机器上,这实际上更慢了,从本地序列化的0.4秒增加到了0.5秒。 - Brian Leishman

5

首先,您需要改变程序的工作方式。将CSV文件分成更小的块。我假设这是一个IP数据存储。

将所有IP地址转换为整数或长整数。

因此,如果有查询,您就可以知道要查找哪一部分。有 <?php ip2long() /* and */ long2ip(); 函数可以完成此操作。因此,从0到2^32,将所有IP地址转换为5000K/50K共100个较小的文件。这种方法可以使序列化更快。

聪明思考,代码整洁 ;)


4

你的问题的答案似乎是否定的。

即使你发现了一个“二进制序列化格式”的选项,很可能对于你所设想的速度仍然太慢。

因此,你可能需要考虑使用(如其他人所提到的)数据库、memcached或在线网络服务。

我还想补充以下几点想法:

  • 请求/响应的缓存
  • 你的PHP脚本不关闭,而是成为一个网络服务器来回答查询
  • 或者,勇敢地说,改变你当前正在使用的数据结构和查询方法

你有一个丰富的数据源,提供许多创意想法,我相信你会想出非常流畅的东西。 - zaf

2

我在这里看到两个选项

字符串序列化,最简单的形式是像这样的东西

  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
2
那么,恐怕这不是一个选项。:-\ - Tomalak
@Tomalak,我想在Stack Overflow上向您寻求帮助,关于如何通过引用将字节数组传递给COM对象方法的无关问题。这是链接:http://stackoverflow.com/questions/42189245/how-to-pass-an-array-of-bytes-reference-to-a-com-object-method当我浏览互联网时,我发现有人发布了相关问题,他们也陷入了同样的困境,链接在这里:https://bugs.php.net/bug.php?id=41286&thanks=3如果您不介意,请借助您的专业知识向我展示如何做到这一点。非常感谢您的帮助。 - Joseph

1

如果你想要速度,直接读写文件系统并不是最优的选择。

在大多数情况下,数据库服务器能够比 PHP 脚本更高效地存储和检索数据。

另一个可能的选择是像 Memcached 这样的东西。

对象序列化并不以其性能而闻名,而是以其易用性,它绝对不适合处理大量的数据。


PHP是否没有二进制序列化格式,可以将内存字节写入磁盘并简单地读取它们?如果CSV文件中全部都是字符串,并且索引对象实际上包含的信息比文本文件少,为什么它的序列化形式必须如此臃肿? - Tomalak
@Tomalak:请查看pack/unpack。 - Robert
@Robert:看起来 pack 只适用于单个值,而不适用于复杂对象。 - Tomalak
@tomalak:序列化较慢,因为它在处理对象和类时会执行许多不易察觉的操作。此外,它还大量依赖递归来构建嵌套数据结构的字符串表示形式,这也可能会很慢。我认为,当你已经拥有面向表格的数据(csv)时,关系型数据库是最好的选择。 - selfawaresoup

0

使用类似JSON的格式来存储/加载数据怎么样?我不知道PHP中JSON解析器的速度有多快,但在大多数语言中,它通常是一个快速操作,并且它是一种轻量级格式。

http://php.net/manual/en/book.json.php


是的,这对于数据是可行的,但不适用于对象图。我正在寻找将整个对象图转储到磁盘的东西,这样重新创建它就没有任何惩罚(在解析、错误检查、对象构造方面)。 - Tomalak
JSON 不能表示引用。它可以表示层级结构。即使没有循环引用,只要有“父级”引用,就结束了。此外,序列化/反序列化绝对不是我考虑的内容。 - Tomalak
你是对的,它无法表示引用。虽然 parent 引用会使对象图成为循环的,即能够到达之前曾经到达过的某个地方。嗯...你可以有一个 sibling 引用,它仍然是非循环的,使我之前的陈述是错误的。 - Daniel Beardsley
我不知道关于速度快还是内存高效,但是我有一个几乎工作正常的JSON序列化器(和反序列化器)实现,它支持循环引用。我不知道这是否符合你的需求 - 我的直觉是,你处理的数据量可能更适合存储在数据库中。 - mindplay.dk
JSON的限制是json_encode要求字符串值采用UTF-8编码。 - Jānis Elmeris

0

SQLite自带PHP,您可以将其用作数据库。否则,您可以尝试使用会话,这样您就不必序列化任何内容,只需保存原始的PHP对象即可。


我能在PHP中的会话之间共享对象吗? - Tomalak
你不能在不同的会话之间共享它。虽然你可以通过设置自定义会话ID来让每个人使用相同的会话,但否则你将不得不考虑使用共享内存。http://php.net/manual/zh/book.shmop.php - Brent Baisley
如果有人偶然发现,请注意 - 不要使用会话来存储大型对象,更不要让多个用户共享同一个会话。这将打破使用会话的初衷 - 而且,由于一次只能有一个用户访问一个会话ID,它将有效地限制请求处理为仅一个!无论如何,会话都必须从磁盘/数据库加载! - SteveB
1
@SteveB 承认,上下文有些模糊,但我之前在多个应用程序中使用过共享/固定会话中的大型数据集。如果您正在构建非典型的应用程序,则非典型的解决方案通常是好的选择。 - hiburn8
1
@hiburn8 我同意这个观点。如果你正在解决一个特定的问题,那么这可能是一个明智的想法。探索每个可用的选项是我所尊重的。我可能基于我的经验有些偏见。 - SteveB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接