连载中 18/22

深分页与 count:limit 100000,10 和 count(*) 的优化套路

2026-05-11 · 7838 阅读 · 0 评论 · 0 赞

越翻越慢的订单列表

老王的运营后台有个怪现象:订单列表第一页秒开,翻到第 5000 页要 6 秒。同一张表、同一个索引,慢的不是查询本身,是深分页——这篇连同 count 统计一起,把两大查询慢性病的病理和药方讲全。

深分页为什么慢

LIMIT 100000, 10 的真实成本:没有「跳过」这个动作——数据库老老实实扫出前 100010 行,扔掉 10 万行,只留 10 行。走二级索引还要每行回表(第 3 篇),10 万次回表就是 10 万次 B+ 树查找。偏移量越大越慢,线性恶化。

药方一:游标分页(首选)

# 第一页
SELECT id, order_no, amount, create_time FROM orders
WHERE user_id = 9527
ORDER BY id DESC LIMIT 10;

# 下一页:带上上一页最后一行的 id
SELECT id, order_no, amount, create_time FROM orders
WHERE user_id = 9527 AND id < 987654
ORDER BY id DESC LIMIT 10;

把「跳过 10 万行」变成「从 id=987654 位置直接开始」——B+ 树一次定位,成本恒定,翻多深都一样快。代价是只支持连续翻页,不能直接跳第 5000 页。移动端无限下拉、管理端「上一页/下一页」,游标分页都是最优解。

药方二:延迟关联

SELECT o.id, o.order_no, o.amount, o.create_time
FROM orders o
JOIN (SELECT id FROM orders
      WHERE user_id = 9527
      ORDER BY id DESC LIMIT 100000, 10) tmp ON o.id = tmp.id;

产品坚持要页码跳转时的救命方案。核心思路:子查询只让索引干活——select id 恰好是二级索引自带的列,覆盖索引扫描完 100010 个 id 也不回表;外层只对 10 个最终 id 回表取整行。10 万次回表变 10 次,量级差异。

count(*) 的真实成本

带条件的 count 无法回避——要数就得扫(或扫索引),这是 MVCC 的代价:每个事务能看见的行都不一样,InnoDB 存不了全局统一的行数(MyISAM 那个免费的 count(*) 是没有并发版本概念的特权)。几个辨析:

  • count(*)、count(1)、count(id):性能基本等价,count(*) 是官方推荐的写法,别再纠结;
  • count(col):语义不同——不统计 col 为 NULL 的行,需要遍历取出值判断,多数场景反而更慢;
  • 给 count 配一个细窄的二级索引(EXPLAIN 里 key 选最小的那棵树)是免费的优化;
  • EXPLAIN rows 是估算值,误差可达几十倍,适合监控报警,不适合精确展示

大表计数方案选型

方案做法适用
直接 count带窄索引扫百万级以内,低频统计
估算值EXPLAIN rows / information_schema 统计大盘展示「约 xx 条」
计数表业务写入时同事务增减计数行要求精确且高频读
Redis INCR计数放 Redis,定时对账校准超高频读、容忍秒级偏差(呼应 Redis 系列的 Write Behind)

老王的订单列表最终落地方案:移动端游标分页,管理端延迟关联 + 页码限制(超过 1000 页引导改用筛选条件),总数用计数表。三件套下去,第 5000 页和第一页一样快。

读的病治完,下一篇治「改」的病:千万级大表加个字段,为什么会把线上卡到报警?Online DDL 与 gh-ost 登场。

咖啡凉了,记得趁热喝。

503

10 年全栈工程师 · 503咖啡馆主理人

#MySQL#深分页#游标分页#延迟关联#count

评论 (0)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 4 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞