广告效果数据分页不能裸用 Limit + Offset,因时间戳密集导致排序不稳定、OFFSET 性能骤降、关联查询放大;须用唯一组合排序字段+联合索引、游标分页、隔离 Count 查询。

GORM 在广告投放效果数据分页上不能直接用 Paginate,必须手写 Limit + Offset 或切游标分页——因为这类数据写入高频、时间戳密集、常带多维筛选(如 campaign_id、date_range、status),传统分页极易漏数或重复,且 OFFSET 超过 5 万后 MySQL 响应直奔秒级。
为什么广告效果表分页不能裸用 Limit + Offset
广告系统每分钟可能插入数千条曝光/点击记录,created_at 字段常精确到秒甚至毫秒,大量记录共享同一时间戳。此时仅靠 Order("created_at DESC") 排序,数据库无法保证相同时间戳记录的物理顺序稳定,翻页时同一条记录可能出现在第 3 页和第 4 页,或直接消失。
- 常见错误现象:
Offset(10000).Limit(50)查出的数据,和前一页最后一条比对,发现中间跳了 2 条;或者刷新页面,同一页内容变了 - 性能拐点早:广告效果表常达千万级以上,
OFFSET 100000在 MySQL 上需扫描并丢弃前 10 万行,即使有索引,I/O 和 CPU 开销也陡增 - 关联查询放大问题:若用
Preload("Campaign"),GORM 先查 50 条日志,再为每条发一次 JOIN 查询,实际加载数百条 Campaign 数据,内存和 DB 连接数双双飙升
必须显式 Order 并组合唯一排序字段
单靠 created_at 不够,必须补一个确定性次级字段,最稳妥是主键 id,或业务侧能保证唯一的 log_id。
- 正确写法:
Order("created_at DESC, id DESC")或Order("date DESC, hour DESC, id ASC")(若按天/小时分区) - 避免只写
Order("created_at DESC")—— 时间重复时,MySQL 返回顺序不可控,GORM 不会帮你加随机偏移 - 确保该组合字段有联合索引,例如:
ALTER TABLE ad_logs ADD INDEX idx_date_id (date, id);,否则排序走 filesort,分页越往后越慢 - 如果用
time.Time字段(如event_time),注意 MySQL 默认精度是秒,要设为datetime(6)并建索引,否则毫秒级事件全挤在同一秒里
Count 查询必须隔离 DB 实例,且慎用全表统计
广告效果接口常需返回总条数用于分页控件,但 db.Where(...).Count(&total) 若复用主查询链,会继承 Limit/Offset,导致 total 永远 ≤ 每页条数。
- 安全写法:
db.Session(&gorm.Session{NewDB: true}).Model(&AdLog{}).Where(...).Count(&total) - 高并发场景下,总数不准可接受?改用
has_next:查pageSize + 1条,有第pageSize + 1条就置has_next = true,省掉 COUNT - 带多维 WHERE 的 COUNT 很慢?手写子查询并强制走索引:
db.Raw("SELECT COUNT(*) FROM (SELECT 1 FROM ad_logs WHERE date BETWEEN ? AND ? AND campaign_id = ?) t", start, end, cid).Scan(&total) - 别在事务里连做 Count + Find —— 两次查询之间若有新数据写入,总数和当前页数据不一致,广告报表类业务通常可容忍这种“弱一致性”
超 10 万行必须切游标分页,且前端传 last_id + last_time
广告效果数据天然适合游标分页:事件有序、写入只追加、主键单调递增。用 WHERE id > ? ORDER BY id LIMIT 50,数据库只需定位索引位置后取 50 行,不扫前 N 行。
- 首次请求:查
ORDER BY id ASC LIMIT 50,返回结果中取users[len(users)-1].ID作为last_id - 后续请求:前端带
last_id=123456,后端写Where("id > ?", last_id).Order("id ASC").Limit(50) - 时间范围敏感场景(如查昨日数据):用
WHERE (date, id) > (?, ?)复合条件,确保跨天边界不漏数据 - 禁止在游标分页里混
Preload—— 先查出 50 个id列表,再用db.Where("id IN ?", ids).Find(&campaigns)批量加载,避免 N+1
广告效果数据分页最易被忽略的点,是把“排序稳定性”当成理所当然。哪怕加了 Order,只要没覆盖所有可能重复的字段,或索引没建对,翻页就会出错。这不是 GORM 的 bug,而是 SQL 语义和存储引擎行为共同决定的——你得亲手把它钉死。


















