状态码轮询慢是因为即使status有单列索引,updated_at不在索引中,导致每次命中都要回表取值排序;需建联合索引(status, updated_at desc, id)并确保字段、顺序、类型完全匹配才能覆盖索引免回表。

能显著降低延迟,但前提是索引字段顺序和查询结构完全匹配,否则依然回表。
为什么状态码轮询容易慢?
高频轮询通常形如 SELECT id, status, updated_at FROM tasks WHERE status = 2 ORDER BY updated_at DESC LIMIT 50。即使 status 上有单列索引,InnoDB 的二级索引叶子节点只存 status 和主键 id,而 updated_at 不在索引里——每次命中都要回表取 updated_at 做排序,再截取前 50 条。若匹配的 status = 2 行有上万条,就等于回表上万次。
覆盖索引必须包含 ORDER BY 字段
要让这个查询真正免回表,联合索引必须把 ORDER BY 字段也囊括进去,且顺序需满足最左匹配与排序需求:
-
status必须放最左(WHERE 等值条件) -
updated_at必须紧跟其后(ORDER BY DESC 要求索引能天然支持降序扫描) -
id必须包含(因为 SELECT 中显式要id,且它是主键,用于索引定位)
正确建法:CREATE INDEX idx_status_updated_id ON tasks (status, updated_at DESC, id)。注意 MySQL 8.0+ 才支持在索引定义中写 DESC;低于该版本,updated_at 默认升序,但 ORDER BY updated_at DESC 仍可能触发 Using filesort,需验证 EXPLAIN 的 Extra 列是否出现 Using index。
EXPLAIN 验证是否真覆盖
执行 EXPLAIN SELECT id, status, updated_at FROM tasks WHERE status = 2 ORDER BY updated_at DESC LIMIT 50 后,关键看两处:
-
key列显示用了idx_status_updated_id -
Extra列必须是Using index(不是Using index condition或空)
如果 Extra 出现 Using filesort,说明 updated_at 在索引中未按 DESC 存储或版本不支持,排序无法下推到索引层;如果仍是 Using where,大概率是 SELECT 里多写了某个不在索引中的字段(比如误加了 content),导致被迫回表。
轮询场景下的易错点
这类查询常配合时间戳做“增量拉取”,例如加 AND updated_at > '2026-10-14 04:00:00'。此时要注意:
- 范围条件(
>)不能放在等值条件(status = 2)之后还指望索引完全覆盖——updated_at > xxx会截断索引扫描,后续字段(如id)无法被用于索引查找,但仍可被覆盖(只要它在索引定义里) - 但如果写成
WHERE status = 2 AND id > 1000,id就成了第二个等值/范围字段,索引设计就得重新评估顺序 - 避免在
status字段上用函数或隐式转换,例如WHERE CAST(status AS CHAR) = '2'或传入字符串'2'却字段为TINYINT,会导致索引失效
覆盖索引生效的边界很窄:字段一个不能少、顺序一点不能错、类型必须严格一致。线上轮询一旦变慢,优先检查这三点,而不是怀疑数据量或服务器负载。











