页面日超3000万访问量,能否将3条MySQL查询合并为一条?
合并三个MySQL查询的可行方案
嘿,这个问题很常见,尤其是在高访问量的场景下——答案是完全可行的,但得选对方法,还要兼顾性能,毕竟3000万日均访问可不是闹着玩的。
你的三个查询分别对应「用户持仓关联币种」「用户关注列表关联币种」「全局Top30币种」,逻辑上是三个独立的数据集,我们可以用UNION ALL把它们合并成一条查询,同时标记每个数据的来源,方便客户端区分处理。
具体实现代码
-- 标记为持仓数据 SELECT 'holdings' AS data_type, mh.*, c.* FROM `meta_holdings` mh INNER JOIN coins c ON c.cmc_id = mh.cmc_id WHERE mh.`userId` = $userId LIMIT 0, 999 UNION ALL -- 标记为关注列表数据 SELECT 'watchlist' AS data_type, mw.*, c.* FROM `meta_watchlist` mw INNER JOIN coins c ON c.cmc_id = mw.cmc_id WHERE mw.`userId` = $userId LIMIT 0, 999 UNION ALL -- 标记为Top30币种数据,补全前两个查询的用户相关字段为NULL SELECT 'top_ranked' AS data_type, NULL AS userId, NULL AS cmc_id, c.* FROM `coins` c ORDER BY c.`rank` ASC LIMIT 0, 30
关键注意事项
- 用UNION ALL而非UNION:UNION会自动去重,额外增加性能开销,而你的三个数据集通过
data_type标记后不会有重复,用UNION ALL速度更快,适合高并发场景。 - 列数必须匹配:
UNION系列要求所有子查询的列数和数据类型一致,所以第三个查询里要补全前两个查询的userId和cmc_id字段,用NULL填充即可。 - 索引优化是核心:3000万日均访问下,索引不到位直接会拖垮数据库:
- 给
meta_holdings和meta_watchlist建复合索引:(userId, cmc_id),这样WHERE和JOIN都能命中索引,避免全表扫描。 - 给
coins建两个索引:(cmc_id)用于JOIN,(rank)用于排序Top30。
- 给
额外性能建议
因为你不能缓存数据,必须实时查询,所以除了合并查询减少请求次数外,还可以考虑:
- 用读写分离架构,把查询请求分流到只读从库,减轻主库压力。
- 定期分析执行计划,确保每个子查询都走最优索引,没有出现临时表或文件排序。
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

