Pagination 機制

Cursor-based vs Offset-based 分頁對比

比較項目
Cursor-based 分頁
Offset-based 分頁

查詢方式

根據某欄位的值(如 createdAt < 上一筆的時間戳

使用 LIMITOFFSET

效能

✅ 高(尤其是大表)

❌ 慢,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 內部,不直接暴露給使用者

游標需要可預測

用固定欄位如 idcreated_at 會較穩定

Last updated