必须写ORDER BY,因为InnoDB无天然行序,不加时LIMIT返回结果每次可能完全不同;这是存储引擎特性而非bug,显式排序才能保证顺序确定性和分页可靠性。

LIMIT + OFFSET 是唯一实用选择,但必须配 ORDER BY 和索引,否则结果不可靠、性能不可控。
为什么必须写 ORDER BY?
MySQL 的 InnoDB 表没有天然行序,不加 ORDER BY 时每次执行 LIMIT 返回的“第一页”可能完全不同。这不是 bug,是存储引擎特性。
- 即使表只有主键
id,也不代表查询会按id升序返回——除非显式声明ORDER BY id - 如果业务要求倒序展示(如最新订单在前),用
ORDER BY created_at DESC,并确保created_at有索引 - 复合排序要补全:比如
ORDER BY status ASC, updated_at DESC,避免相同status值导致顺序漂移
OFFSET 大了就慢?这是真的,但别急着换方案
当 OFFSET 超过几万,比如 LIMIT 20 OFFSET 100000,MySQL 实际扫描了 100020 行才返回 20 条,EXPLAIN 中的 rows 值会远高于 20——这是典型信号。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 小型项目通常数据量在百万以内,且用户极少翻到 50 页之后;先测真实负载,别一上来就上游标分页
- 优化优先级:先加覆盖索引,例如只查
id和name,再用这些id回表,比SELECT *快得多 - 如果真遇到卡顿,再改用游标分页:前端保存上一页最后一条的
id,下一页查WHERE id > ? ORDER BY id LIMIT 20
别碰 ROW_NUMBER() 模拟分页
MySQL 8.0+ 支持窗口函数,但 ROW_NUMBER() OVER (ORDER BY id) 做分页是反模式。
- 它必须先给全表编号,再过滤,无法提前终止,比
LIMIT多扫全部数据 - 语句更长、可读性差,还容易在低版本 MySQL(如 5.7)直接报错
- 除非你同时需要显示“第 N 条”这种行号,否则纯分页场景完全没必要引入
最容易被忽略的是排序字段的稳定性:如果用 created_at 游标分页,而多条记录恰好同秒插入,就会漏或重——必须补主键,写成 ORDER BY created_at DESC, id DESC,并让前端传两个值。










