【问题标题】:Manipulating big array in a more memory-efficient way以更节省内存的方式操作大数组
【发布时间】:2017-12-13 06:55:18
【问题描述】:

我目前正在为一个与大型数据库交互的应用编写 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 响应时,我也会遇到内存限制错误。

我完全迷失了,我真的需要一次输出这么多的数据,不知道如何提高内存效率(可能有类似缓冲区之类的东西?从来没有深入研究过)。

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

$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;

【问题讨论】:

标签: php arrays memory memory-limit


【解决方案1】:

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

  • 必须很快吗?会不会很慢?
  • 低内存服务器是绝对唯一可用的资源吗?

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

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

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

【讨论】:

  • 感谢您的回答。我确实受到这台服务器的限制,我希望他们把事情搞砸了,服务器应该有更多的内存,但我要等到明天恢复支持时才能确定。我可以研究一下云服务,那将是一次有趣的经历。文件 IO 可能不是一个选项,因为这必须有点快,但我会做一个测试来确定。谢谢!
  • 如果他们希望它更快,他们应该为您提供更多资源,至少可以控制 php.ini :) 祝你好运!
  • BTW - 我不确定您的服务器客户端是如何设置的,但如果此查询是使用 API 调用完成的,您可以尝试简单地继续输出数据(回显)而不是存储它在数组中,这可能有效。另一种解决方法是使用 EventSource,它是某种 Web 套接字,可以保持客户端和服务器之间的连接(需要 html5)
  • 是的,我完全同意,我被老板发邮件要求他们升级到专用服务器。回显数据的想法还不错,但是我需要输出 json,因此需要进行一些工作以确保所有内容都正确格式化,并且不能保证我能够将完整的数据库结果存储在内存中无论如何,在这种情况下,我必须做一些无缓冲的 mysql 请求。现在我设法向 API 发出了多个请求,而不是一个,但这并不理想。
猜你喜欢
  • 2017-11-20
  • 2012-07-14
  • 2019-05-12
  • 2013-10-30
  • 1970-01-01
  • 2011-03-30
  • 2012-09-06
  • 2011-02-11
  • 1970-01-01
相关资源
最近更新 更多