Cursor 的核心概念是「標記目前位置的東西」,而英文用同一個字指三樣不同的東西:畫面上的游標、資料庫逐筆讀取查詢結果的物件、分頁回應裡記錄下一頁從哪裡接續的標記。三者的責任與用法完全不同,讀到時要先從上下文分辨是哪一個。分頁的那一個另有專卡 Pagination Cursor,它底下常用的定位方式見 Keyset Pagination。

概念位置

三個意思落在三個不同的層:

意思本站的中文寫法在哪一層誰持有
畫面上的滑鼠或文字游標游標使用者介面作業系統與瀏覽器
逐筆讀取查詢結果的物件查詢結果的讀取物件(cursor)資料庫與它的用戶端程式庫應用程式向資料庫開啟,用完要關閉
記錄下一頁或下一批從哪裡接續的值接續標記(cursor)API 與逐批讀取的指令API 裡由伺服器發給客戶端、客戶端下一次請求原樣送回;批次程式也可能自己保存它

查詢結果的讀取物件與接續標記常被混在一起,因為 API 分頁在伺服器端的實作有時就建立在資料庫的讀取物件上;但對 API 的使用者來說,他拿到的只是一個接續標記字串,背後是讀取物件、keyset 條件還是別的機制,都是伺服器的內部選擇(見 Pagination Cursor 的不透明性)。

本站不把三者都寫成「游標」:在台灣讀者的日常與軟體介面語境裡,「游標」先指畫面上的那一個,拿來譯讀取物件或接續標記,讀者讀到的會是滑鼠游標。資料庫廠商的中文文件裡用法並不一致,有的寫游標、有的寫資料指標。譯名怎麼選與量測見 #370 借用中文日常詞當術語譯名的誤讀。

可觀察訊號與例子

  • 畫面上的游標:出現在 CSS(cursor: pointer)、終端機的控制序列(隱藏游標、移動游標)、無障礙與輸入設計的討論裡。
  • 查詢結果的讀取物件:出現在資料庫用戶端的程式碼裡,例如 Python 資料庫介面的 conn.cursor()、SQL 的 DECLARE CURSOR、MongoDB 變更串流(change stream)的讀取端。它的特徵是要開、要關:沒關閉的讀取物件會一直佔著資料庫的資源(連線與讀取交易),佔著連線不還累積到池滿就是 Connection Leak。例如 SQLite 在預寫日誌(write-ahead log,WAL)模式下讀取與寫入可以同時進行,但沒讀完、沒關閉的讀取物件會讓讀取交易(read transaction)一直開著;SQLite 官方文件說明,只要有連線還開著讀取交易,檢查點(checkpoint,把 WAL 的內容寫回主資料庫檔的動作)就無法重設 WAL 檔,WAL 檔因此一直變大、查詢跟著變慢。Go 的讀取物件沒關閉時佔住連線、讓後續查詢排隊的實測見 10.1 database/sql:連線池、查詢結果的讀取與交易〈查詢結果讀取物件(*sql.Rows)的關閉與連線洩漏〉。
  • 接續標記:出現在 API 的回應與參數裡(next_cursor、cursor),以及逐批掃描的指令回傳值,例如 Redis 的 SCAN 每次回傳下一次要帶的接續標記,回到 0 代表掃完一輪。它的特徵是一個字串、客戶端原樣送回,客戶端不需要關閉它,但要知道它會不會過期、跨版本還能不能用(見 分頁之爭 的條款清單)。

設計責任

讀文件或程式碼時,先用上面的訊號判斷是哪一個 cursor,再去找對應的知識:讀取物件要處理開關與資源佔用,接續標記要處理格式、有效期與跨版本相容。寫文件時,第一次出現就用中文寫出是哪一個意思並附上英文 cursor,不要只寫英文讓讀者自己判斷,也不要三個意思共用「游標」一個譯名。