SQL查询优化请求:获取关注用户谱面的查询耗时超6秒
优化你的Beatmap查询性能
咱们先拆解下你当前查询慢的核心原因:关联子查询的重复执行、GROUP BY可能触发的临时表/文件排序,以及缺少合适索引导致的全表扫描。下面是针对性的优化方案:
1. 优先优化索引(最核心的一步)
索引是提升查询速度的关键,针对你的查询场景,建议创建以下两个复合索引:
针对
follows表:
你的查询需要过滤follows.uid = 3443963,并关联follows.fid = maps.uid,创建复合索引可以让数据库快速定位匹配行,无需全表扫描:CREATE INDEX idx_follows_uid_fid ON follows (uid, fid);如果想实现覆盖查询(不用回表取
follows.id),可以把id也加入索引:CREATE INDEX idx_follows_uid_fid_id ON follows (uid, fid, id);针对
maps表:
你的查询需要按sid分组找最新的add_time,同时过滤特定uid的记录,创建包含uid、sid、add_time的复合索引,能让数据库快速过滤并排序:CREATE INDEX idx_maps_uid_sid_addtime ON maps (uid, sid, add_time DESC);这个索引可以同时满足:过滤关注用户的maps、按sid分组取最新时间、最终排序的需求。
2. 重写查询,用窗口函数替代关联子查询+GROUP BY
原来的关联子查询(SELECT max(add_time) FROM maps i WHERE i.sid = maps.sid)会对主查询的每一行执行一次,非常低效。改用窗口函数可以一次性完成分组取最新记录的操作:
SELECT m.id as mapid, m.uid, m.add_time, -- 只选你实际需要的字段,别用SELECT *,减少数据传输和索引回表 m.sid -- 其他你需要的字段... FROM ( SELECT *, -- 按sid分组,给每条记录按add_time降序排号,最新的那条行号为1 ROW_NUMBER() OVER (PARTITION BY sid ORDER BY add_time DESC) as rn FROM maps -- 先获取所有关注用户的ID,过滤maps的uid WHERE uid IN (SELECT fid FROM follows WHERE uid = 3443963) AND uid != 3443963 ) m WHERE m.rn = 1 ORDER BY m.add_time DESC LIMIT 9;
这种写法的优势:
- 只扫描
maps表一次,避免了关联子查询的重复执行 - 用
ROW_NUMBER()替代GROUP BY,避免了临时表和文件排序的开销 - 逻辑更清晰,也符合现代SQL的最佳实践
3. 简化JOIN逻辑
你原来用LEFT JOIN然后WHERE follows.id IS NOT NULL,这其实等价于INNER JOIN——因为LEFT JOIN保留的不匹配行都会被WHERE条件过滤掉。改成INNER JOIN能让数据库优化器生成更高效的执行计划:
如果坚持用原来的JOIN写法,可以改成:
SELECT -- 只选需要的字段 m.id as mapid, m.uid, m.add_time, m.sid FROM maps m INNER JOIN follows f ON f.fid = m.uid AND f.uid = 3443963 WHERE m.uid != 3443963 AND m.add_time = (SELECT MAX(add_time) FROM maps i WHERE i.sid = m.sid) GROUP BY m.sid ORDER BY m.add_time DESC LIMIT 9;
4. 额外小技巧
- 避免
SELECT *:只选择你实际需要的字段,这样如果索引包含这些字段,数据库可以直接从索引取数据(覆盖索引),不用回表查询原数据,速度会快很多。 - 查看执行计划:用
EXPLAIN命令查看你的查询执行计划,看看是否有Using filesort、Using temporary或者ALL(全表扫描)的标记,这些都是需要优化的点。
内容的提问来源于stack exchange,提问作者Daniel Yeffet
相关产品推荐
相关产品推荐

