已采纳
回答正文
回复
讨论正文
该问题的正文与回答。
MySQL 8.0.32 / InnoDB,单主 32C/128G,innodb_buffer_pool_size=64G。核心表 t_msg_log 约 1.2 亿行,PK=id,二级索引 idx_user_ts (user_id, created_at),其中 user_id 高基数(单值 300~2 万行不等),created_at 是 DATETIME。
高频查询:
SELECT id, user_id, created_at
FROM t_msg_log
WHERE user_id = ? AND created_at BETWEEN ? AND ?
ORDER BY created_at DESC
LIMIT 20;
对 user_id 命中 2000~20000 行、时间窗 7 天的查询,EXPLAIN 稳定显示 type=ref, key=idx_user_ts, Extra=Using index; Using filesort——走索引扫描后还要 filesort。但把 WHERE 里的时间窗放宽(命中行数从 1 万涨到 5 万),优化器反而切回 type=index, key=idx_user_ts, Extra=Using index(直接索引序扫描 + LIMIT 提前终止)。也就是说:命中行数越多,反而不需要 filesort;命中行数适中的中间态,优化器偏偏选了更贵的路径。我怀疑是 max_seeks_for_key 的代价模型在这个区间把「索引扫 + filesort」估成了更便宜的一条,但无法证实。
SET optimizer_trace_max_mem_size 打开 trace 对比两条路径的 rows_examined_per_range / cost,trace 里能看到优化器把 ref 路径的 cost 估成约 1.8 万,index 路径约 4.2 万——但 cost 数字与实际执行耗时对不上(实测 ref+filesort 反而比 index 慢 3~5 倍)。SET max_seeks_for_key=1 强制走 ref:确实切回 ref+filesort,但 P99 更差,确认这条路径在这个行数区间就是错的。sort_buffer_size 从 256K 调到 4M,filesort 没落盘(tmp_disk_tables 没涨),但 CPU 排序本身仍是主要开销。IGNORE INDEX(idx_user_ts),强制全表扫描,慢到不可用,排除。idx_user_ts_id (user_id, created_at, id):filesort 仍在,只是回表消失,P99 只降 20%。max_seeks_for_key、range_optimizer_max_mem_size、max_length_for_sort_data 哪个在这条路径上生效)?有没有可复现的最小复现 SQL 能让我自己验证?FORCE INDEX 兜底?参考:站内已有人问过 (a,b) 联合索引 + WHERE a IN + ORDER BY b 触发 filesort(id=187),以及 filesort 落盘的排查(id=41),但都没覆盖「命中行数中等区间、优化器代价模型选错路径」这个具体场景。