需求:按時間降序分頁查詢,但查詢過程中不斷有新數據插入。傳統 LIMIT offset, pageSize 會導致:
不使用 OFFSET,改用 上一頁最後一條記錄的主鍵/時間戳 作為游標:
// 第一頁:無游標,按時間降序取前 N 條
SELECT * FROM table ORDER BY create_time DESC LIMIT 10;
// 後續頁:攜帶上一頁最後一條記錄的 create_time(或 id)
SELECT * FROM table
WHERE create_time < :lastCreateTime -- 嚴格小於,避免重複
ORDER BY create_time DESC LIMIT 10;
Criteria criteria = session.createCriteria(Entity.class);
criteria.addOrder(Order.desc("createTime"));
if (lastCreateTime != null) {
criteria.add(Restrictions.lt("createTime", lastCreateTime));
}
criteria.setMaxResults(pageSize);
List<Entity> list = criteria.list();
public Page<Entity> findByCursor(Date lastCreateTime, int pageSize) {
Specification<Entity> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (lastCreateTime != null) {
predicates.add(cb.lessThan(root.get("createTime"), lastCreateTime));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
Pageable pageable = PageRequest.of(0, pageSize, sort);
return repository.findAll(spec, pageable);
}
ScrollableResults scroll = session.createNativeQuery(sql)
.setParameter("lastTime", lastCreateTime)
.setMaxResults(pageSize)
.scroll(ScrollMode.FORWARD_ONLY);
// 流式處理,避免全量加載
create_time, id)。< 或 >,避免邊界重複。| 方案 | 優點 | 缺點 |
|---|---|---|
| 游標分頁 | 無重複/遺漏、性能穩定 | 不支持跳轉指定頁碼 |
| OFFSET + 快照隔離級別 | 支持隨機翻頁 | 需數據庫支持可重複讀/快照隔離,高併發下鎖競爭大 |
| 標記已讀 ID 集合 | 邏輯簡單 | 內存開銷大,不適用長列表 |