whereIn 大数据,内存占用问题


// 取 order 表的部分符合条件的 uuid ,此处为简单示例直接取 all
$orderUUIDs = Order::all()->pluck('order_uuid');
 info('内存记录:'.memory_get_usage());
// 代码执行到下面这句内存溢出,实际使用时 $orderUUIDs 大概有十几万条
OrderFee::whereIn('order_uuid', $orderUUIDs)->sum('fee');

以上代码输出

[2022-07-27 18:36:02] production.INFO: 内存记录:51784224
[2022-07-27 18:36:02] production.ERROR: Allowed memory size of 134217728 bytes exhausted (tried to allocate 1052672 bytes) {"exception":"[object] (Symfony\\Component\\ErrorHandler\\Error\\FatalError(code: 0): Allowed memory size of 134217728 bytes exhausted (tried to allocate 1052672 bytes) at /apps/storage/20220720181003/vendor/laravel/framework/src/Illuminate/Collections/Arr.php:558)

问题:MySQL in 怎么这么耗内存,我只是取一个聚合结果

我未曾珍惜的,我不再拥有?‍?️
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 21

$orderUUIDs 大概有十几万条 php不内存溢出 、sql长度设置小了 语句也会执行不了 、wherein 十几万意义在哪

1年前 评论

如果用子查询呢

1年前 评论
cevin 1年前

如果有联合索引还好点,没索引全表扫描了

1年前 评论

一条SQL可以完成的没必要拆开来写,建议用Join连接查询

1年前 评论

这是架构设计上的问题了,这些数据可以通过定时任务或消息队列中去分析,这样可以将查询的任务拆分,不至于影响到其他业务。亦或是通过 MQ 将数据写入支持 OLAP 的一些数据库,如 Clickhouse。

1年前 评论

wherein 如果数据过大 还是用join把,不然你那么多数据传过去 mysql的tcp都顶不住

1年前 评论
晏南风 4个月前
陈先生

按照楼主描述 数据查出来十几万条数据, 楼层有人回答索引的问题, 殊不知在 in 一个非常大的范围的时候, 索引会无效. 我相对会建议 chunk + 索引.

1年前 评论
jiangjun

这是php内存爆了,不是mysql内存爆了。看情况mysql是返回了数据的。

1年前 评论

这种大批量的数据建议不要在服务中实时计算,耗时太严重,可以通过定时任务刷新数据到缓存中,服务直接读取缓存中的数据

1年前 评论
sanders

OrderFee 先与 Order 建立模型关系,在使用 whereHas 进行查询。

1年前 评论

优化

$model =  Order::select('order_uuid');
$orders = Db::select($model->toSql(),$model->getBindings());
$orderUUIDs = array_column($orders,'order_uuid');
OrderFee::whereIn('order_uuid', $orderUUIDs)->sum('fee');

之所以转成原生DB,是因为model 很占用内存。

问题

这个错误属于php

Order::all()->pluck('order_uuid')

(就算是为了演示,也不能这么写啊 Order::pluck('order_uuid') 都比这个好

十万条数据,就是10万个类

ps:我感觉不是whereIn问题,因为不科学。

1年前 评论
lyxxxh (作者) 1年前

奇奇怪怪的设计

1年前 评论

where in 十几万太多了,建议数据写到统计表,查起来就快了。

1年前 评论

用子查询,这样不用取到PHP的内存里,然后PHP里用迭代器

1年前 评论

优化一:

$uids = select order_uuid  from Order; 

优化二:

collect($uids)->chunk(500)->map(function($item, $key){
        return OrderFee::whereIn('order_uuid', $item)->sum('fee');
})->sum();

定义为Command/消费金额统计命令,然后注册到任务调度里面去统计。
如果你直接实时的话,很容易阻塞worker进程无法响应nginx。

也可以使用chunkById这种方式,区别在于这种类似于命令行分页查询, where id > xxx limit 500运行。

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!