網頁應用程式通常會將資料分頁顯示給使用者。使用者會收到一頁結果,當他們前往下一頁時,系統會擷取並顯示下一批結果。本頁說明如何在 Spanner 中執行全文搜尋時,為搜尋結果新增分頁功能。
分頁選項
在 Spanner 中實作分頁查詢的方式有兩種:以鍵值為準的分頁 (建議) 和以位移為準的分頁。
以鍵值為基礎的分頁機制可確保要求之間結果一致,同時以較小、更易於管理的方式擷取搜尋結果。頁面最後一個結果的專屬 ID (「鍵」) 會做為參照點,用於擷取下一組結果。
Spanner 通常建議使用以鍵為準的分頁。雖然實作以位移為準的分頁功能較為簡單,但有兩項重大缺點:
- 查詢費用較高:以位移為準的分頁會重複擷取並捨棄相同結果,導致費用增加,效能降低。
- 結果不一致:在分頁查詢中,每個網頁通常會在不同的讀取時間戳記擷取。舉例來說,第一頁可能來自下午 1 點的查詢,下一頁則來自下午 1 點 10 分的查詢。也就是說,搜尋結果可能會在查詢之間變更,導致各網頁的結果不一致。
另一方面,以鍵值為基礎的分頁功能會使用頁面最後一個結果的不重複 ID (鍵值),擷取下一組結果。即使基礎資料有所變更,也能確保有效率地擷取資料,並獲得一致的結果。
為確保網頁結果穩定,應用程式可能會在相同時間戳記發出不同網頁的所有查詢。不過,如果查詢超過版本保留期限 (預設為 1 小時),這項作業可能會失敗。舉例來說,如果 version_gc 為一小時,而使用者在下午 1 點擷取第一批結果,並在下午 3 點點選「下一步」,就會發生這類失敗情況。
使用以鍵為準的分頁
以鍵值為準的分頁會記住前一頁的最後一個項目,並將其做為下一頁查詢的起點。如要達成這個目的,查詢必須傳回 ORDER BY 子句中指定的資料欄,並使用 LIMIT 限制資料列數。
如要使用以鍵為準的分頁功能,查詢必須依據嚴格的總排序順序排序結果。最簡單的方法是選擇任何總訂單,然後視需要新增決勝局資料欄。在大多數情況下,總排序是搜尋索引排序順序,而資料欄的唯一組合是基本資料表主鍵。
使用我們的 Albums 範例結構定義,第一頁的查詢如下所示:
GoogleSQL
SELECT AlbumId, ReleaseTimestamp
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")
ORDER BY ReleaseTimestamp DESC, AlbumId
LIMIT 10;
PostgreSQL
SELECT albumid, releasetimestamp
FROM albums
WHERE spanner.search(albumtitle_tokens, 'fifth symphony')
ORDER BY releasetimestamp DESC, albumid
LIMIT 10;
由於 ReleaseTimestamp 不是鍵,因此 AlbumId 是平手時的決勝點。可能會有兩個不同的專輯,但 ReleaseTimestamp 的值相同。
如要繼續,應用程式會再次執行相同查詢,但使用 WHERE 子句限制前一頁的結果。額外條件必須考量主要方向 (遞增與遞減)、同分決勝,以及可為空值的資料欄的 NULL 值順序。
在我們的範例中,AlbumId 是唯一的鍵資料欄 (遞增順序),且不得為 NULL,因此條件如下:
GoogleSQL
SELECT AlbumId, ReleaseTimestamp
FROM Albums
WHERE (ReleaseTimestamp < @last_page_release_timestamp
OR (ReleaseTimestamp = @last_page_release_timestamp
AND AlbumId > @last_page_album_id))
AND SEARCH(AlbumTitle_Tokens, @p)
ORDER BY ReleaseTimestamp DESC, AlbumId ASC
LIMIT @page_size;
PostgreSQL
本範例使用查詢參數 $1、$2、$3 和 $4,這些參數分別繫結至為 last_page_release_timestamp、last_page_album_id、query 和 page_size 指定的值。
SELECT albumid, releasetimestamp
FROM albums
WHERE (releasetimestamp < $1
OR (releasetimestamp = $1
AND albumid > $2))
AND spanner.search(albumtitle_tokens, $3)
ORDER BY releasetimestamp DESC, albumid ASC
LIMIT $4;
Spanner 會將這類條件解讀為「可搜尋」。也就是說,Spanner 不會讀取您篩除的文件索引。這項最佳化功能可大幅提升以鍵值為準的分頁效率,遠勝以位移為準的分頁。
使用以偏移量為準的分頁
以位移為準的分頁功能會運用 SQL 查詢的 LIMIT 和 OFFSET 子句來模擬頁面。LIMIT 值表示每頁的結果數。
第一頁的 OFFSET 值設為零,第二頁設為頁面大小,第三頁則設為頁面大小的兩倍。
舉例來說,以下查詢會擷取第三頁,頁面大小為 50:
GoogleSQL
SELECT AlbumId
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")
ORDER BY ReleaseTimestamp DESC, AlbumId
LIMIT 50 OFFSET 100;
PostgreSQL
SELECT albumid
FROM albums
WHERE spanner.search(albumtitle_tokens, 'fifth symphony')
ORDER BY releasetimestamp DESC, albumid
LIMIT 50 OFFSET 100;
使用須知:
- 強烈建議使用
ORDER BY子句,確保各網頁的排序方式一致。 - 在正式版查詢中,請使用查詢參數 (而非常數) 指定
LIMIT和OFFSET,提高查詢快取效率。詳情請參閱「查詢參數」。
後續步驟
- 瞭解如何排序搜尋結果。
- 瞭解如何執行子字串搜尋。
- 瞭解如何混合使用全文和非文字查詢。
- 瞭解如何搜尋多個資料欄。