MySQL 深分页 LIMIT 1000000,50 越翻越慢,游标分页和延迟关联到底怎么选
该问题的正文、回答与评论。
背景:MySQL 8.0.36,InnoDB,一张订单表 order 约 1.2 亿行,主键 id,二级索引 idx_status (status)、idx_status_create (status, create_time)。列表页按 status 过滤后按 create_time 倒序翻页,典型查询:
SELECT id, order_no, amount, create_time FROM order WHERE status='paid' ORDER BY create_time DESC LIMIT 1000000, 50;
现象:前 10 页(offset 0~500)P99 约 80ms,可一到 offset 100 万级,单条就飙到 2.3s,且越往后越慢,EXPLAIN 显示 type=ref、key=idx_status_create,但 rows 估算 900 万+,Extra 里有 Using filesort。
已尝试:1) 把查询改写成覆盖索引(只取索引列)+ 子查询先取 id 再回表(延迟关联),offset 100 万时降到约 700ms,但 300 万还是 1.8s;2) 调大 sort_buffer_size 到 8M,基本没变化;3) 想过直接加复合索引 (status, create_time, id),但担心写放大。
想得到的答案形态:1) 在「必须支持跳页(用户能直接点第 20000 页)」和「可以改成上一页/下一页」两种产品约束下,分别推荐哪种方案(延迟关联 / 游标分页 WHERE create_time < :last / 记忆化 id 区间),并给出各自的可复现 SQL 写法与适用边界;2) 游标分页对「并发写入导致 create_time 重复」的去重/漏数据问题怎么兜底;3) 如果业务确实强需求任意跳页,除深分页外还有哪些量级上更稳的替代(如 ES 导出、id 段预切片),各自的成本量级。