13

假设您有以下整数数组:

array(1, 2, 1, 0, 0, 1, 2, 4, 3, 2, [...] );

整数最多可达一百万个条目;只是它们没有被硬编码,而是预先生成并存储在 JSON 格式的文件中(大小约为 2MB)。这些整数的顺序很重要,我不能每次都随机生成它,因为它应该是一致的并且总是在相同的索引处具有相同的值。

如果之后在 PHP 中读回该文件(例如使用file_get_contents+ json_decode),则需要 700 到 900 毫秒才能将数组取回——“好吧”我想,“这可能是合理的,因为json_decode必须解析大约 200 万个字符,让我们缓存它” . APC 将其缓存在大约 68MB 的条目中,可能是正常的,zval 很大。然而,从 APC 取回这个数组也需要 600 毫秒,这在我看来还是太多了。

编辑:APC 确实序列化/反序列化以存储和检索具有一百万个项目数组的内容是一个漫长而繁重的过程。

所以问题:

  • 如果我打算在 PHP 中加载一百万个条目数组,无论是数据存储还是方法,我是否应该期待这种延迟?据我了解,APC 存储 zval 本身,因此理论上从 APC 检索它应该尽可能快(无解析,无转换,无磁盘访问)

  • 为什么 APC 对于看似简单的事情如此缓慢?

  • 有没有任何有效的方法可以使用 PHP 将一百万个条目数组完全加载到内存中?假设 RAM 使用没有问题。

  • 如果我要根据索引仅访问该数组的切片(例如,将块从索引 15 加载到索引 76)并且实际上永远不会将整个数组放在内存中(是的,我知道这是明智的做法,但我想知道所有方面),对于完整的阵列,最有效的数据存储系统是什么?显然不是 RDBM;我在考虑redis,但我很乐意听到其他想法。

4

4 回答 4

3

假设整数都是 0-15。然后每个字节可以存储 2 个:

<?php
$data = '';
for ($i = 0; $i < 500000; ++$i)
  $data .= chr(mt_rand(0, 255));

echo serialize($data);

跑步:php ints.php > ints.ser

现在您有一个包含 500000 字节字符串的文件,其中包含 1,000,000 个从 0 到 15 的随机整数。

装载:

<?php
$data = unserialize(file_get_contents('ints.ser'));

function get_data_at($data, $i)
{
  $data = ord($data[$i >> 1]);

  return ($i & 1) ? $data & 0xf : $data >> 4;
}

for ($i = 0; $i < 1000; ++$i)
  echo get_data_at($data, $i), "\n";

我机器上的加载时间约为 0.002 秒。

当然,这可能并不直接适用于您的情况,但它比包含一百万个条目的臃肿 PHP 数组要快得多。坦率地说,在 PHP 中拥有这么大的数组从来都不是正确的解决方案。

我也不是说这也是正确的解决方案,但如果它符合您的参数,它绝对是可行的。

请注意,如果您的数组有 0-255 范围内的整数,您可以摆脱包装,只需将数据作为ord($data[$i]). 在这种情况下,您的字符串将是 1M 字节长。

最后,根据文档file_get_contents(),php会对文件进行内存映射。如果是这样,您最好的表现是将原始字节转储到文件中,并像这样使用它:

$ints = file_get_contents('ints.raw');
echo ord($ints[25]);

这假设它ints.raw正好是一百万字节长。

于 2012-07-28T17:46:24.290 回答
2

APC 存储序列化的数据,因此在从 APC 加载回数据时必须对其进行反序列化。这就是您的开销所在。

加载它的最有效方法是以 PHP 和 include() 的形式写入文件,但是对于包含一百万个元素的数组,你永远不会有任何效率水平......它需要大量的内存,并且加载需要时间。这就是发明数据库的原因,那么数据库有什么问题?

编辑

如果您想加快序列化/反序列化,请查看igbinary扩展

于 2012-07-28T15:20:33.900 回答
1

正如 Mark 所说,这就是创建数据库的原因——允许您根据常规使用模式有效地搜索(和操作,但您可能不需要)数据。它也可能比使用数组实现自己的搜索更快。我猜我们正在谈论每次访问数组时都会序列化和反序列化的接近 2-300MB 的数据(序列化之前)。

如果您想加快速度,请尝试分别分配数组的每个元素 - 您可能会用函数调用开销换取序列化所花费的时间。你也可以用你自己的扩展来扩展它,将你的数据集包装在一个小的检索界面中。

我猜你不能直接存储 zval 的原因是因为它们包含内部状态,你根本不能只将变量符号表指向前一个表。

于 2012-07-28T15:36:39.190 回答
1

我不能每次都随机生成它,因为它应该是一致的,并且在相同的索引处始终具有相同的值。

你读过伪随机数吗?有一个叫做种子的小东西可以解决这个问题。

还要对您的选择和主张进行基准测试。您是否对 file_get_contents 与 json_decode 进行了计时?这里需要在存储和访问成本之间进行权衡。例如。如果您的数字是 0..9(或 0..255),那么将它们存储在 2Mb 字符串中并在此使用访问函数可能会更容易。无论是从 FS 还是 APC,2Mb 的加载速度都会更快。

于 2012-07-28T15:32:24.760 回答