API超时真凶!百万数据扫描0产出!
一睁眼,你的生产环境报警群炸了——接口响应时间超过30秒!
并不是被DDoS攻击,仅仅是一个运营小妹想在后台查看第10000页的历史订单。
就像资深DBA常挂在嘴边的那句狠话: “盲目使用OFFSET分页,就是给数据库判了死缓。”
今天我们就来深扒一下 PostgreSQL(PG)里关于分页的那些“爱恨情仇”,看看你的代码是不是正在裸奔。
最熟悉的陌生人
每一个写过 SQL 的兄弟,大概都是从 LIMIT 10 OFFSET 0 开始入坑的。
这语法太符合直觉了,前台传个 page_no,后台算一下偏移量,代码写起来如丝般顺滑。
但问题来了,数据库真的觉得顺滑吗?
当你执行 OFFSET 1000000 LIMIT 10 时,你以为 PG 只是乖乖跳到了第100万行,然后取了10条数据?
大错特错。
PostgreSQL 的引擎机制决定了,它必须先扫描完前 1000010 行数据,然后把前 1000000 行扔进垃圾桶,只留最后 10 行给你。
什么概念?
为了拿这10条数据,你让 CPU 白白做了10万倍的功。这就是经典的 复杂度陷阱。
随着页码越翻越深,查询时间呈线性爆炸式增长。数据不会撒谎:在百万级数据表中,OFFSET 0 耗时可能只有 1ms,但到了 OFFSET 1000000,耗时可能直接飙升到 5秒甚至更多。
这哪里是查询,简直是在“谋杀”你的 I/O 资源。
像书签一样快
既然硬翻书太累,聪明的工程师想出了**“书签大法” ,也就是我们常说的游标分页(Keyset Pagination)**。
如果你用过 Twitter 或者刷过朋友圈,你会发现它们永远是“下拉加载更多”,而不是让你选“第几页”。
这就是游标分页的主场。
它的原理简单粗暴:不告诉数据库“跳过多少行”,而是告诉它“上一页最后那条数据的 ID 是多少”。
SQL 画风突变:
WHERE id > last_seen_id LIMIT 10
这波操作有多秀?
利用索引(Index),数据库可以直接定位到 last_seen_id 的位置,然后顺手往后拿10条。
无论你的数据是 1 万行还是 1 亿行,无论你翻到第几页,查询复杂度始终稳定在 或 。
这就好比你看书,OFFSET 是每次都要从第一页数到第五百页;而游标分页,是直接翻到你上次夹书签的那一页。
效率提升不是一倍两倍,而是指数级的降维打击。
没有银弹
听到这,是不是想回去把所有代码重构了?
先别急,技术圈没有银弹。
虽然游标分页在性能上吊打传统分页,但它有两个致命的**“阿喀琉斯之踵”**:
- 告别“随机跳转” :你无法直接跳到“第 50 页”。因为你不知道第 49 页的最后一条 ID 是什么。
- 实现门槛高:如果你的排序字段不是唯一的(比如按“价格”排序),你还得引入主键 ID 做二级排序,否则分页数据会乱套。
我们来看一个残酷的对比表:
| 维度 | LIMIT / OFFSET | 游标分页 |
|---|---|---|
| 深度分页性能 | 极差(拖垮数据库) | 极好(毫秒级响应) |
| 实现难度 | 实习生都会 | 需要资深开发设计 |
| 随机跳转 | 支持 | 不支持 |
| 数据一致性 | 差(翻页间隙新增数据会导致重复) | 完美(天然适应动态数据) |
场景决定生死
所以,到底该选哪一个?这里直接给出一份**“保命决策指南”**:
场景一:后台管理系统
数据量也就几万条,运营人员需要快速跳转到第 10 页查看数据。
结论: 放心用 OFFSET。这点数据量 PG 还是扛得住的,别为了性能牺牲业务便利性。
场景二:C端信息流/评论区
类似于抖音、微博,用户只会无限下拉,数据量千万级甚至亿级。
结论: 必须使用游标分页。这是 Google、Stripe 等大厂 API 的标准设计规范。如果你在这里用 OFFSET,流量上来那一刻就是系统崩盘之时。
场景三:数据量大,但必须用 OFFSET
如果产品经理拿刀架在你脖子上非要“随机跳转”,怎么办?
救命稻草: 使用**“延迟关联”**(Late Row Lookup)。
先只查 ID 进行分页(走索引,快),拿到 10 个 ID 后,再回表去查具体数据。这能避免数据库在扫描丢弃那 100万行时,去读取庞大的数据文件,能救你一命。
最后的忠告
技术没有绝对的好坏,只有适不适合。
当你下周一坐在工位上时,不妨打开慢查询日志(Slow Query Log)看一眼。如果有大量的 OFFSET 语句耗时超过 1 秒,恭喜你,你抓到了一只潜在的性能“吞金兽”。
理解数据库的扫描代价,是后端工程师进阶的基本功,别让简单的分页成为你职业生涯的绊脚石。
最后留个悬念:
除了分页,你的索引可能也在“假装工作”。下期我们来聊聊《PostgreSQL 索引失效的十大离谱场景》,每一个都是血泪坑。
互动时刻:
你们公司的项目中,遇到过最奇葩的分页需求是什么?有没有人试过 OFFSET 一千万的?评论区晒出你的“惨痛经历”!