PHP和万阵宝贝

时间:2012-07-28 15:13:28

标签: php apc large-data

想象一下,你有以下整数数组:

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 个答案:

答案 0 :(得分: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字节字符串的文件,其中包含从0到15的1,000,000个随机整数。

加载:

<?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正好是一百万字节长。

答案 1 :(得分:2)

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

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

修改

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

答案 2 :(得分:1)

  

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

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

同时评估您的选项和声明。你有没有计时file_get_contents与json_decode?这里需要在存储和访问成本之间进行权衡。例如。如果您的数字是0..9(或0..255),那么将它们存储在2Mb字符串中并使用访问函数可能更容易。无论是从FS还是APC,2Mb的加载速度都会更快。

答案 3 :(得分:1)

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

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

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