首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >大型SQL查询在PHP中占用的内存是SQL数据的10倍

大型SQL查询在PHP中占用的内存是SQL数据的10倍
EN

Stack Overflow用户
提问于 2022-09-08 16:55:16
回答 1查看 60关注 0票数 3

因此,我们有一个现有的系统,我们试图扩大和运行内存,检索近3M记录。

我试图通过确定查询返回的数据大小,通过执行以下操作来确定增加服务器内存作为停止间隙解决方案的可行性:

代码语言:javascript
复制
select sum(row_size) 
from (
SELECT
    ifnull(LENGTH(qr.id), 0)+
    ifnull(LENGTH(qr.question_id), 0)+
    ifnull(LENGTH(qr.form_response_id), 0)+
    ifnull(LENGTH(qr.`value`), 0)+
    ifnull(LENGTH(qr.deleted_at), 0)+
    ifnull(LENGTH(qr.created_at), 0)+
    ifnull(LENGTH(qr.updated_at), 0)
    as row_size
    FROM
    ....
    LIMIT 500000
) as tbl1;

它返回30512865,大约是30 is的数据。

但是,当我交叉检查PHP实际使用什么来存储结果时,使用:

代码语言:javascript
复制
$memBefore = memory_get_usage();
$formResponses = DB::select($responsesSQL, $questionIDsForSQL);
$memAfter = memory_get_usage();
dd($memBefore, $memAfter);

我得到了31537755222403248,这意味着存储简单数组的292974304字节或大约300 of的内存使用量!

我想了解为什么内存占用是检索到的数据的10倍,除了修改后端和前端的API响应之外,我还能做些什么来减少内存占用吗?

对于上下文,当前实现使用上述结果(由getQuestionResponses返回)将其转换为由question_id使用Laravel集合分组的关联数组:collect($this->questionResponseRepo->getQuestionResponses($questions))->groupBy('question_id')->toArray();

我正在考虑用自己的实现替换collect,这将使用从查询返回的数组来通过将该数组转换为Laravel的Collection来降低内存膨胀,但这仍然无助于该数组本身占用300 of的500 k记录响应而不是30 of的记录响应。

在线解决方案之一是使用SplFixedArray,但我不知道如何强制DB::select使用它而不是数组?

另一种可能的解决方案是确保返回简单的assoc数组,而不是标准类https://stackoverflow.com/a/37532052/373091的数组,但当我尝试这样做时,如下所示:

代码语言:javascript
复制
// get original model
$fetchMode = DB::getFetchMode();
// set mode to custom
DB::setFetchMode(\PDO::FETCH_ASSOC);
$memBefore = memory_get_usage();
$formResponses = DB::select($responsesSQL, $questionIDsForSQL);
DB::setFetchMode($fetchMode);
$memAfter = memory_get_usage();
dd($memBefore, $memAfter, $formResponses);

,我得到了错误Call to undefined method Illuminate\\Database\\MySqlConnection::getFetchMode(),这意味着它显然不能再从Laravel> 5.4 :(

有什么建议吗?

EN

回答 1

Stack Overflow用户

发布于 2022-09-08 17:34:14

我认为真正的问题是,您正在同时将所有300万条记录加载到内存中。相反,您应该以块的形式处理它们,或者使用游标。

块状

要将记录分批,可以使用Laravel的块块方法。该方法接受两个参数,即块大小和一个回调,该回调将传递用于处理的模型或对象的子集。这将在每个块的查询上执行。

下面是从文档中摘录的示例:

代码语言:javascript
复制
Flight::chunk(200, function ($flights) {
    foreach ($flights as $flight) {
        //
    }
});

游标

或者,如果只想执行单个查询,也可以使用游标方法。在这种情况下,Laravel一次只能补充一个模型,所以一次内存中永远不会有一个以上的模型(如果没有使用雄辩的话,也不会有多个对象)。

代码语言:javascript
复制
foreach (Flight::where('destination', 'Zurich')->cursor() as $flight) {
    //
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73652637

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档