limit offset, size 在千万级表上卡死是因为mysql需扫描offset+size行并丢弃前offset行,导致大量无用io和cpu消耗。

为什么 LIMIT offset, size 在千万级表上会卡死?
因为 MySQL 不跳行,它老老实实从头开始扫描,数到 offset + size 行,再丢掉前 offset 行。比如 LIMIT 1000000, 10,就要读取并丢弃 100 万条记录——每条都可能触发一次回表(随机 IO),CPU、内存、磁盘全在干无用功。
常见错误现象:EXPLAIN 显示 type=range 或 type=index,但执行时间仍长达数秒甚至几十秒;SHOW PROFILE 显示大量 Handler_read_next 和 Handler_read_rnd_next;慢查询日志里反复出现同一类深分页语句。
- 排序字段没索引 → 触发
filesort,千万级数据极易写磁盘临时文件 -
SELECT *+ 非覆盖索引 → 每行都要回表,IO 成倍放大 - 主键不连续或有删除 →
id BETWEEN x AND y这种“伪游标”会漏数据或重复
延迟关联(Deferred Join)怎么写才真正生效?
核心不是套个子查询,而是让子查询只走索引、不回表,并且外层 JOIN 只查 10 条(不是 100 万条)。
假设你查的是 t_order 表,按 create_time DESC 分页,且 status = 'PAID':
SELECT t1.* FROM t_order t1 INNER JOIN ( SELECT id FROM t_order WHERE status = 'PAID' ORDER BY create_time DESC LIMIT 1000000, 10 ) t2 ON t1.id = t2.id;
关键前提必须满足:
- 子查询里只查
id,不能带其他字段,否则无法走覆盖索引 - 必须有联合索引,如
INDEX idx_status_ctime (status, create_time)—— 字段顺序不能错 - 外层
t1.*的字段数越多,网络和内存开销越大;能选具体字段就别用*
如果没建对索引,这个写法照样慢。它只是把“扫 100 万行 + 回表”变成“扫 100 万行索引 + 10 次回表”,但扫索引本身仍有成本。
游标分页(Seek Method)必须避开的三个坑
它快,是因为把 LIMIT offset 换成了 WHERE sort_col > last_value,MySQL 直接用索引定位起始点。但实际落地时容易栽在细节上。
- 排序字段必须唯一:如果
create_time有重复,WHERE create_time > '2023-01-01'可能跳过同时间的多条记录,或重复返回 —— 必须补一个唯一字段,例如ORDER BY create_time DESC, id DESC - 前端传参要校验:不能直接拼接用户传来的
last_id,得先查一遍确认该 ID 存在且属于当前查询条件范围内,否则可能越界或空结果 - 不能用于跳页:比如用户点“第 500 页”,你没法用游标实现;这种场景只能退回到延迟关联,或加缓存预热
典型安全写法:
SELECT id, title, create_time FROM t_article
WHERE status = 1
AND (create_time, id) > ('2023-10-01 12:00:00', 12345)
ORDER BY create_time DESC, id DESC
LIMIT 20;
注意括号里的元组比较,比分开写 create_time >= ... AND id > ... 更严谨。
COUNT 总数要不要查?怎么查才不拖垮性能?
千万级表上,SELECT COUNT(*) FROM t WHERE ... 往往比查数据还慢,尤其带模糊匹配(LIKE '%xxx%')时,基本等于全表扫描。
- 后台列表页如果非显示总数不可,优先用缓存:比如每小时异步跑一次
COUNT写入 Redis,误差可接受 - 用户搜索场景下,用
SQL_CALC_FOUND_ROWS已被废弃,且在高并发下锁表风险高,别用 - 全文检索能替代 LIKE:建
FULLTEXT索引后,MATCH(...) AGAINST(...)的 COUNT 效率提升百倍,但语义不同 - 如果业务允许“估算”,可用
SHOW TABLE STATUS的rows值(误差 ±10%),或采样估算(SELECT COUNT(*) * 10 FROM t TABLESAMPLE BERNOULLI (10))
最常被忽略的一点:前端分页控件显示“共 1234567 条”,对用户几乎无实际价值,反而逼你每次都要算总数。砍掉这个需求,性能立竿见影。











