以更节省内存的方式操作大数组

Manipulating big array in a more memory-efficient way

我目前正在为一个与大型数据库交互并需要从中检索大量数据的应用程序编写一个 API,然后将其输出为 JSON。

我正在使用 CodeIgniter (CI) 作为数据库接口,但我认为它与此无关。我 运行 进入内存限制,不幸的是我无法增加限制,因为共享托管服务不会这样做。

我从数据库中得到了大约 56k 行,这些行被 CI 放入一个数组中(零索引,非常标准)。每行有 7 个字段。

在我开始遍历数组以修改数据之前,一切都很好。脚本在几次循环迭代后遇到内存限制错误,即使我只是修改原始数组,而不是分配新变量,我想。

Allowed memory size of 134217728 bytes exhausted

下面是我使用的代码:

$query = $this->db->get('table');
if ($query->num_rows() > 0) {
    $result = $query->result_array();
    foreach ($result as $k => $v) {
        foreach($v as $key => $value) {
            if ($key === 'column_name') {
                $result[$k][$key] = json_decode($value);
                continue;
            }
            if ($value == null) {
                $result[$k][$key] = '';
            } else if (ctype_digit($value)) {
                $result[$k][$key] = (int) $result[$k][$key];
            }
        }
    }
    return $result;
}

只是解码一些 json 并转换为整数或空字符串,没什么特别的。但是我会在任何改变 $result 数组的行上得到内存限制错误。即使我删除了(内存密集型)json_decode,我仍然会在网上收到一个简单地转换为 int.

的错误

此外,即使我删除了整个 foreach,稍后当我使用 json_encode 生成 API 响应时,我也会遇到内存限制错误。

我完全迷路了,我真的需要一次输出这么多数据,不知道如何提高内存效率(也许使用类似缓冲区或其他东西?从来没有深入研究过这个)。

编辑:对于任何感兴趣的人,我设法通过对数据库进行无缓冲查询来减少内存使用量。这样,数组中只存储了一份数据副本。我还删除了 foreach 并专门处理每个字段。然而,主要问题可能是如何 。这是新代码:

$query = $this->db->get('table');
$result = [];
while ($row = $query->unbuffered_row('array')) {
    if ($row['column1'] == '[]') {
        $row['column1'] = [];
    } else {
        $row['column1'] = json_decode($row['column1']);
    }
    $row['column2'] = (int) $row['column2'];
    $row['column3'] = (int) $row['column3'];
    $row['column4'] = is_null($row['column4']) ? '' : (int) $row['column4'];
    $row['column5'] = is_null($row['column5']) ? '' : (int) $row['column5'];

    $result[] = $row;
}

return $result;

有很多方法可以解决这个问题,真正的问题是您的优先事项是什么?

  • 一定要快吗?能不能慢点?
  • 低内存服务器是绝对唯一可用的资源吗?

理想的解决方案显然是升级您的服务器,假设您有消耗大量内存的任务,这应该是 运行 这个项目的任何人都关心的问题。

显然有使用微服务的现代方法,每个微服务处理一大块数据。它们可以由您编写,也可以使用 AWS 等云服务。

话虽如此,假设你确实局限于当前的星座,你根本别无选择,只能使用有限的内存服务器来处理大数据,我会推荐使用本地文件I/O -这不是最快的解决方案,但如果您读取数据块并将它们写入临时文件,您将解决内存问题,然后您可以将该文件刷新到客户端。