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 怎么这么耗内存,我只是取一个聚合结果

我未曾珍惜的,我不再拥有?‍?️
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 20

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

3周前 评论

如果用子查询呢

3周前 评论
cevin 2周前

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

3周前 评论

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

2周前 评论

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

2周前 评论

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

2周前 评论
陈先生

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

2周前 评论
jiangjun

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

2周前 评论

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

2周前 评论
sanders

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

2周前 评论

优化

$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问题,因为不科学。

2周前 评论
lyxxxh (作者) 2周前

奇奇怪怪的设计

2周前 评论

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

1周前 评论

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

2天前 评论

优化一:

$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运行。

9小时前 评论

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