如何加速千万级bugs表按token、reported_at查title的SELECT SQL速度
问题根因分析
你的查询执行慢主要有三个核心原因:
- 现有索引设计不符合当前查询的最左前缀匹配规则,过滤效率极低:你目前的联合索引为
(category, token, reported_at),但查询条件没有传入category字段,数据库无法利用这个索引快速定位token和reported_at匹配的记录。你看到的“查询用到索引”实际是全索引扫描,扫描行数远高于实际符合条件的行数。 - 存在回表开销:即使数据库通过索引定位到了符合条件的记录id,现有索引不包含你需要查询的
title字段,必须回到主键索引逐行读取title数据,大量随机IO会严重拖慢查询速度。 - 查询写法存在冗余和逻辑问题:你当前写的嵌套子查询没有实际意义,子查询结果集仅包含
id字段,外层直接查询title既没有关联条件也没有对应字段,不仅会增加优化器解析成本,甚至可能触发不符合预期的笛卡尔积或语法报错。
优化方案
按照优先级从高到低执行即可:
1. 先修正SQL写法
去掉无意义的嵌套子查询,直接写单表查询即可,减少不必要的开销:
SELECT title FROM bugs WHERE reported_at = '2020-08-30' AND token = 'token660';
如果确实需要通过子查询过滤id,必须加上id关联条件,比如:
SELECT b.title FROM bugs b INNER JOIN ( SELECT id FROM bugs WHERE reported_at = '2020-08-30' AND token = 'token660' ) t ON b.id = t.id;
但这种写法相比直接单表查询没有任何收益,不推荐使用。
2. 创建适配查询场景的覆盖索引(核心优化)
直接创建包含过滤条件和返回字段的联合覆盖索引,让查询可以直接从索引中拿到所有需要的数据,完全避免回表:
CREATE INDEX idx_bugs_token_reportedat_title ON bugs(token, reported_at, title);
索引设计逻辑:
- 将两个等值查询的过滤列
token、reported_at放在索引最前列,数据库可以通过索引直接定位到所有符合条件的记录,扫描范围极小 - 把需要返回的
title列放在索引末尾,整个查询不需要访问主键表数据,索引扫描即可完成所有逻辑,随机IO转为顺序IO,在千万级表上性能通常可以提升10~100倍。
如果你的业务中大量查询都会同时带上category过滤条件,也可以将索引调整为(token, reported_at, category, title),不会影响当前查询的效率,同时可以兼容更多查询场景。新索引创建完成后,如果没有其他查询强依赖旧的(category, token, reported_at)索引,建议删除旧索引,减少写入时的索引维护开销。
3. 辅助优化项
- 确认
reported_at字段为DATE/DATETIME类型,不要用字符串存储时间,避免隐式类型转换导致索引失效 - 如果单条件过滤后的结果集超过1万条,加上
LIMIT分页读取,不要一次性拉取全量数据,减少扫描和网络传输开销 - 索引创建完成后执行
ANALYZE TABLE bugs;更新表统计信息,避免优化器因为统计信息不准选错执行计划
内容的提问来源于stack exchange,提问作者AYehia0
相关产品推荐
相关产品推荐

