直接执行SQL耗时0.01秒,封装为view后查询耗时3秒的原因及优化方法
问题产生原因
- 视图算法限制:你的视图使用了
UNION ALL语法,MySQL无法对这类视图使用MERGE(合并)算法,只能默认采用TEMPTABLE(临时表)算法:会先将视图内UNION ALL的全部查询结果计算完成后写入临时表,再对外返回查询结果。而你直接执行原始查询时,结果可以流式返回,无需等待全量结果计算完成,感知上会快很多;如果临时表数据量超出内存临时表上限,还会转存为磁盘临时表,进一步拖慢速度。 - 查询缓存干扰:直接执行原始SQL时大概率命中了MySQL的查询缓存,所以耗时仅0.01秒,你可以给原始查询加上
SQL_NO_CACHE参数(SELECT SQL_NO_CACHE ...)测试真实耗时,排除缓存干扰。 - 条件无法下推:如果后续查询视图时带过滤条件,采用
TEMPTABLE算法的视图无法将外层过滤条件下推到内层的子查询中,只能全量扫临时表过滤,性能会进一步下降。
优化方案
- 补充覆盖索引:给查询涉及的关联、过滤字段加索引,避免回表查询:
- 给
club_transactions加联合索引idx_status_biz_club(status, business_id, club_id, amount, transaction_date, daily_percentage),覆盖第二个子查询的过滤、关联、查询字段 - 确保
pos_transactions.business_id、pos_transactions.psp_id、businesses.id、businesses.sub_category_id、psps.id、clubs.id等关联字段都存在索引
- 给
- 调整临时表参数:适当调大MySQL配置中的
tmp_table_size和max_heap_table_size,尽可能让临时表保存在内存中,避免转为磁盘临时表 - 预计算结果表:如果业务对数据实时性要求不高,可以用定时任务定时将视图的全量结果同步到一张实体汇总表中,后续查询直接访问汇总表,性能可以和直接执行原始SQL持平
- 拆分查询逻辑:如果业务场景允许,不要直接
SELECT *查询全量视图,尽可能按需拆分查询,比如单独查pos交易、club交易的逻辑,避免每次都做全量UNION ALL计算
内容的提问来源于stack exchange,提问作者amirhosein hadi
相关产品推荐
相关产品推荐

