Pagination 機制
Cursor-based vs Offset-based 分頁對比
比較項目
Cursor-based 分頁
Offset-based 分頁
查詢方式
根據某欄位的值(如 createdAt < 上一筆的時間戳
)
使用 LIMIT
與 OFFSET
效能
✅ 高(尤其是大表)
❌ 慢,OFFSET 數越大查詢越慢
穩定性
✅ 穩定,不易漏資料或重複
❌ 當資料變動時容易漏/重複資料
適合用於
聊天、社群動態、API
分頁表格、小型資料表
範例說明
✅ Cursor-based 分頁(以時間戳為游標):
SELECT * FROM posts
WHERE created_at < '2024-05-21T10:00:00Z'
ORDER BY created_at DESC
LIMIT 10;
created_at
就是游標欄位每次取得 10 筆比上一筆更舊的資料
前端記得下次請求要帶回最後一筆資料的游標(例如:
?cursor=2024-05-21T10:00:00Z
)
❌ Offset-based 分頁(傳統):
SELECT * FROM posts
ORDER BY created_at DESC
LIMIT 10 OFFSET 20;
代表取得第 3 頁的資料(每頁 10 筆)
問題:
資料新增/刪除時,offset 的資料位置可能改變
OFFSET 數值大時效率會變差(DB 仍要掃前面 n 筆資料)
游標來源可以是什麼?
一般為唯一且排序穩定的欄位
id
(單調遞增)createdAt
/updatedAt
有時也會用 base64 編碼的游標(如:
?cursor=eyJpZCI6MTIzfQ== (編碼過的 ID = 123)
📦 使用場景
使用情境
是否適合 Cursor
社群動態串(如 Facebook、Twitter)
✅ 非常適合
聊天記錄滾動
✅ 非常適合
資料表後台管理(跳頁)
❌ 不適合,跳頁難實作
REST / GraphQL API 查詢
✅ 標準做法(Relay 就是 Cursor-based)
✨ 優點總結
✅ 高效能(避免 OFFSET 開銷) ✅ 分頁穩定(不會因資料變動而漏資料) ✅ 使用者體驗好(無限捲動最佳解)
⚠️ 缺點與挑戰
問題
解法建議
無法跳頁(只能上一頁/下一頁)
通常搭配 infinite scroll,不用「跳頁」
實作複雜度較高
封裝在 API 內部,不直接暴露給使用者
游標需要可預測
用固定欄位如 id
、created_at
會較穩定
Last updated