假设您有以下整数数组:
array(1, 2, 1, 0, 0, 1, 2, 4, 3, 2, [...] );
整数最多有一百万个条目;只是它们不是硬编码的,而是预先生成并存储在JSON格式的文件中(大小约为2MB)。这些整数的顺序很重要,我不能每次都随机生成它,因为它应该是一致的,并且在相同的索引上总是有相同的值。
如果这个文件后来在PHP中读回来(例如使用file_get_contents
+json_decode
),只需700到900ms就可以取回数组——"好吧"我想,"这可能是合理的,因为json_decode
必须解析大约200万个字符,让我们缓存它"。APC将其缓存在一个大约需要68MB的条目中,这可能是正常的,zval很大然而,从APC取回这个阵列也需要一些不错的600毫秒,在我看来这仍然太多了。
编辑:APC会序列化/取消序列化来存储和检索内容,而使用百万项数组是一个漫长而繁重的过程
所以问题是:
如果我打算在PHP中加载一个一百万条目的数组,无论是数据存储还是方法,我应该预料到这种延迟吗?据我所知,APC存储zval本身,因此理论上从APC检索它应该尽可能快(没有解析,没有转换,没有磁盘访问)为什么APC对看似简单的事情如此缓慢有没有什么有效的方法可以使用PHP在内存中完全加载一百万个条目的数组?假设RAM的使用不是问题。
如果我只访问基于索引的这个数组的切片(例如,将块从索引15加载到索引76),而从未将整个数组实际存储在内存中(是的,我知道这是一种合理的方法,但我想了解所有方面),那么对于整个数组来说,什么是最有效的数据存储系统?显然不是RDBM;我在考虑redis,但我很乐意听到其他想法。
假设整数都是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字节的字符串,其中包含1000000个从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
正好是一百万字节长。
APC存储序列化的数据,因此在从APC加载回时必须取消序列化。这就是你的开销所在。
加载它最有效的方法是以PHP和include()的形式写入文件,但对于包含一百万个元素的数组,您永远不会有任何级别的效率。。。它需要大量的内存,加载也需要时间。这就是数据库被发明的原因,那么你对数据库有什么问题呢?
编辑
如果您想加快序列化/反序列化的速度,请查看igbinary扩展
我不能每次都随机生成它,因为它应该是一致的,并且在相同的索引上总是有相同的值。
你读过伪随机数吗?有一种叫做种子的小东西可以解决这个问题。
还要对您的期权和索赔进行基准测试。您是否对file_get_contents与json_decode进行了计时?这里需要在存储成本和访问成本之间进行权衡。例如,如果你的数字是0..9(或0..255),那么将它们存储在2Mb字符串中并使用访问函数可能会更容易。2Mb将更快地加载,无论是从FS还是APC。
正如Mark所说,这就是创建数据库的原因——允许您根据常规使用模式有效地搜索(和操作,但您可能不需要)数据。它也可能比使用数组实现自己的搜索更快。我猜我们谈论的是每次访问数组时,大约有2-300MB的数据(在序列化之前)被序列化和未序列化。
如果您想加快速度,请尝试分别分配数组的每个元素——您可能会用函数调用开销来换取序列化所花费的时间。您也可以使用自己的扩展进行扩展,将数据集封装在一个小型检索接口中。
我猜你不能直接存储zval的原因是因为它们包含内部状态,而且你不能直接将变量符号表指向上一个表。