分页查询必须传 page 和 size 并校验:page<1 设为 1,size 限 1–100;须用 ORDER BY 保证稳定性,推荐游标分页;GORM 中 Count 需隔离条件;大数据量应避 OFFSET 改用游标或覆盖索引。
分页查询必须传
和
,不能只靠
前端
传偏移量
后端自己算
容易出错,尤其当
为 0 或负数、
超过上限时,数据库可能直接报错或返回空结果。Golang 中应统一校验并转换:
默认为 1,小于 1 则设为 1
应限制范围(如 1–100),超出则截断或返回错误
用
计算,避免前端恶意传大偏移量导致慢查询
示例片段:
用
做分页时,务必加
MySQL / PostgreSQL 等对无序分页不保证稳定性:同一查询多次执行可能因索引顺序、MVCC 版本不同而跳过或重复记录。尤其在高并发写入场景下,漏数据很常见。
没有
的
不是真正分页,只是“取前 N 条”
推荐用主键或唯一时间字段排序,例如
或
若业务允许,优先考虑游标分页(
),避免
性能衰减
用
时,
/
不会自动处理总数,需手动
的
+
只负责查数据,不附带总条数。但多数分页 UI 需要显示“共 XX 条”“第 X 页”。别用
直接套在带
的链式查询上——它会忽略
、
甚至部分
条件。
立即学习
“
go语言免费学习笔记(深入)
”;
正确做法:拆成两个查询,或用子查询封装条件
简单场景可用
防止复用原 query 的 limit/offset
复杂关联查询建议手写
,用
执行,更可控
大数据
量下
分页变慢?换游标或覆盖索引
当
超过 10 万行,MySQL 可能扫描并丢弃大量数据,响应从毫秒升到秒级。这不是 Golang 层能优化的,得从 SQL 和索引入手。
游标分页要求前端传上一页最后一条的排序字段值(如
),SQL 改为
确保排序字段有索引;若用多字段排序(如
),索引必须包含全部字段且顺序一致
避免
,只查必要字段,减少网络和内存开销;大文本字段(如
)可单独
异步加载
游标分页不能跳页,但对“下一页”“上一页”场景足够高效,也规避了
的偏移漂移问题。
pagesizeoffsetpagesizepagesizeoffset = (page - 1) * sizeif page < 1 {
page = 1
}
if size < 1 || size > 100 {
size = 20
}
offset := (page - 1) * sizesql.DB.QueryORDER BYORDER BYLIMIT OFFSETORDER BY id ASCORDER BY created_at DESC, id DESCWHERE created_at < ? AND id < ?OFFSETgormLimitOffsetCOUNT(*)gormLimitOffsetdb.Count(&total)WHEREOrderJoinsWheredb.Session(&gorm.Session{NewDB: true}).Model(&User{}).Where(...).Count(&total)SELECT COUNT(*) FROM (SELECT ... ) AS tRawOFFSETOFFSETlast_created_atWHERE created_at < ? ORDER BY created_at DESC LIMIT 20ORDER BY status, created_atSELECT *TEXTOFFSET