我的内存不足,在PHP中使用80k x 20矩阵(数组)的整数值。有解决方案吗?
背景
我有一个PHP应用程序,它收集数据并将其存储到数据库中。数据收集在不同的域中(> 20k)。变量的数量因域而异(主要是无限制的),所以我必须在我的MySQL数据库中存储以逗号分隔的列表(在版本5之前)。这表现得很好。
在某些时候,用户需要下载数据。下载功能必须执行一些规范化,因此它需要每个变量的中位数(不是平均值!)(实际上是变量子集的中位数)。通常我可以轻松地从数据库中读取数据,爆炸()逗号分隔数据并将中值相关数据存储到数组[var] [row]中。我可以排序()数组,我得到了中位数。
但是,有一个域没有100或1000个数据记录(行),但是80K。给定20个中值相关变量,这是1.6M整数值(32位)或51 MB原始整数数据(可能是两倍,因为我正在使用64位Linux机器)。到目前为止,这么好 - 但阵列结构有一些开销,所以它变得比128 MB大得多。这就是我的PHP内存不足的地方。
我不想做的事
当然,我可以增加每个PHP脚本的内存限制。出于各种原因,我想避免这种情况。
还有一些算法不需要存储n个值来计算中位数,但是对n / 2(+ x)感到满意,但是将内存负载减少到50%+ X可能不足以解决问题
我还可以计算每个变量的中位数变量。但这需要我从数据库加载80K行数据20次并再次执行explode()。这将大大增加脚本运行时间。
[编辑]数据库当前未规范化(使用每个数据行的CSV数据)。出于性能原因,这是有意和必要的。因此,我不喜欢规范化数据库,因为这将导致一个包含100M条目和巨大索引的表。
我想做什么
我们所说的不超过51 MB的原始32位整数值。是否有任何变化可以将开销减少到几个百分点?甚至可能在64位机器上?
我知道自PHP 5.0.0以来可用的SPL扩展,但我还没有找到解决方案如何使用此扩展来节省内存。任何人都可以给我一个提示 - 通过SPL或使用其他解决方案(理想情况下在PHP中可用)?
示例代码
private function retrieveReferences() {
$query = $this->getResultsQuery(true);
$times = array();
$tp = -1; // Length of $times - 1
while ($row = $query->fetchArray()) {
$timeSrc = explode(',', $row['times']);
// Store the times per page
foreach ($timeSrc as $p=>$s) {
// Should be faster than checking isset $times[$p] all the time
while ($p > $tp) {
$times[] = array();
$tp = count($times) - 1;
}
$times[$p][] = (int)$s;
}
}
// Compute median for each $times[$p]
// <snip>
}
答案 0 :(得分:0)
数组至少消耗104 bytes,因此数组越少,内存使用量就越少。
切换到1维数组会节省一些内存。只需使用$times[$item*$n_items + $sub_item]
而非$times[$item][$sub_item]
计算的索引访问元素。
完成后,很容易切换到SplFixedArrays
,这几乎可以消除标准数组的内存开销(标准数组每个元素最多消耗90 bytes)。
你甚至可以通过打包字符串中的所有整数来消除zval开销
答案 1 :(得分:0)
在http://we-love-php.blogspot.de/2012/06/php-memory-consumption-with-arrays.html上找到的解决方案实际上证明非常有用。我认为值得,发布在这里:
PHP中最有效的数组类型是字符串。
实际上在字符串中存储大量整数值(每个整数4个字节或字符)可以节省超过80%(!)的内存。当然有很多缺点:你需要对整数进行编码/解码,排序需要自定义函数等。因此,只有当内存真正重要时,这个解决方案才有意义。
就我而言,16位整数就足够了,所以我可以用2个字符对每个整数进行编码。以下是我使用的示例代码:
private function retrieveReferences() {
$query = $this->getResultsQuery(true);
$timesSE = array();
$tp = -1; // Length of $times - 1
while ($row = $query->fetchArray()) {
$timeSrc = explode(',', $row['times']);
// Store the times per page
foreach ($timeSrc as $p=>$s) {
// Should be faster than checking isset $times[$p] all the time
while ($p > $tp) {
$timesSE[] = '';
$tp = count($timesSE) - 1;
}
// Save to limit to 16bit (in this specific case)
$i = (int)$s;
if ($i > 0xFFFF) {
$i = 0xFFFF;
}
$timesSE[$p].= chr(($i & 0xFF00) >> 8).chr($i & 0x00FF);
}
}
// Compute median for each $times[$p]
// <snip>
}
答案 2 :(得分:0)
您可能会达到内存限制,因为数据最低为6.5M
需要看看你的php.ini文件。最初默认为8M
请参阅http://www.php.net/manual/en/ini.core.php#ini.memory-limit