相同MySQL查询在Laravel中运行缓慢但phpMyAdmin中极快的优化问题
问题根因分析
- phpMyAdmin默认自动追加分页限制:你在phpMyAdmin执行无LIMIT的查询时,工具默认会加
LIMIT 25这类分页参数,实际只返回前几十条数据,所以耗时极低。你可以在phpMyAdmin的查询结果页底部看到「显示行 0 - 24 (总计 X 行, 查询花费 0.0030 秒)」,3ms是返回前25条的速度,你点击「显示所有」再执行查询,耗时就会和Laravel侧一致。 - 排序对总耗时影响可以忽略:你去掉排序后Laravel耗时没有下降,是因为排序1900条数据的耗时不到10ms,占总耗时的比例极低,99%的耗时都集中在750万行大表的关联扫描环节。
- 索引缺失导致全表扫描:你的关联查询没有匹配的联合索引,大表每次查询都要全表扫描,是耗时高的核心原因。
优化方案
- 先验证全量查询的实际耗时
在phpMyAdmin执行查询时手动加SQL_NO_CACHE跳过查询缓存,同时加强制全量返回的LIMIT参数,绕开phpMyAdmin默认分页,确认真实耗时:
SELECT SQL_NO_CACHE `discounts`.`id`, `discounts`.`discount` FROM `discounts` LEFT JOIN `discount_companies_criteria` ON `discounts`.`id` = `discount_companies_criteria`.`discount_id` AND `discount_companies_criteria`.`is_active` = 1 WHERE `discount_companies_criteria`.`company_id` IS NULL AND `discounts`.`status` = 1 GROUP BY `discounts`.`id` LIMIT 18446744073709551615;
- 添加联合索引避免全表扫描
给750万行的大表建覆盖关联、过滤条件的联合索引,避免回表查询:
CREATE INDEX idx_dcc_discount_active_company ON discount_companies_criteria (discount_id, is_active, company_id);
同时给discounts表的status字段加索引(如果未添加):
CREATE INDEX idx_discounts_status ON discounts (status);
加索引后全量查询耗时会直接降到10ms以内。
- 优化查询逻辑提升效率
你当前的逻辑是查找没有匹配活跃公司规则的折扣,用NOT EXISTS替代LEFT JOIN + GROUP BY的写法,大表场景下效率更高,Laravel代码参考:
DB::table('discounts') ->select('id', 'discount') ->where('status', 1) ->whereNotExists(function ($query) { $query->select(DB::raw(1)) ->from('discount_companies_criteria') ->whereColumn('discount_companies_criteria.discount_id', 'discounts.id') ->where('is_active', 1); }) ->orderBy('discount', 'ASC') // 需要排序再加,不需要可移除 ->get();
- 按需添加分页
如果业务场景不需要一次性返回所有1900条数据,加paginate(20)做分页查询,每次只返回需要的条数,耗时会进一步降低。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

