每個 Firestore (Datastore 模式) 查詢會使用一或多個「索引」計算其結果,這些索引當中包含由索引的屬性及實體的祖系 (選用) 所指定序列中的實體索引鍵。索引會不斷更新,以反映應用程式對其實體所做的任何變更,因此所有查詢均可獲得正確的結果,無需再進一步計算。
索引分為兩種類型:
- 內建索引
- 根據預設,Datastore 模式資料庫會自動為每一個實體種類的每一種屬性預先定義一個索引。這些單一屬性索引適用於簡單的查詢類型。
- 複合式索引
- 複合式索引會針對每個索引實體的多個屬性值建立索引。複合式索引支援複合式查詢,需要使用索引設定檔 (
index.yaml
) 進行定義。
後文將深入探討索引類型。
索引定義和結構
索引的定義是以指定實體種類的屬性清單為準,每個屬性各有其對應順序 (遞增或遞減)。如用於祖系查詢,索引也可以選擇是否包含實體的祖系。
索引包含索引定義中指定名稱的每一項屬性。索引中的每個項目代表一個實體,這些實體可能是以索引為基礎的查詢結果。只有在實體為索引中使用的每個屬性設定索引值時,才會納入索引。如果索引定義參照實體沒有值的屬性,該實體就不會出現在索引中,因此永遠不會做為任何索引查詢的結果傳回。
複合式索引會先按祖系排序,再按屬性值排序,排序順序則以索引定義為準。根據這項瞭解,您可以建立「完美索引」,提高查詢效率。
索引設定
Firestore (Datastore 模式) 提供「內建」(或稱自動) 索引,適用於以下形式的查詢:
- 僅使用祖系和索引鍵篩選條件的無種類查詢
- 只使用祖系和等式篩選條件的查詢
- 只使用不等式篩選條件的查詢 (只限單一屬性)
- 只使用祖系篩選條件、等式篩選條件 (查詢屬性),以及不等式篩選條件 (查詢金鑰) 的查詢
- 沒有篩選條件,且只有一個按遞增或遞減順序查詢屬性的排序順序
舉例說明,Datastore 模式資料庫預設會自動為每一個實體種類的每個屬性預先定義兩個單一屬性索引,其中一個按遞增順序排序,另一個則按遞減順序排序。若您不希望資料庫留存屬性的索引,請將該屬性排除於您的索引之外。請注意,排除屬性會將該屬性自任何複合式索引移除。
內建索引足以執行許多簡單查詢,例如只使用等式的查詢以及簡單的不等式查詢。
內建索引不會出現在 Google Cloud 控制台的「索引」頁面中。
若要進行較複雜的查詢,應用程式必須定義「複合式」(亦稱「手動」) 索引。下列形式的查詢必須使用複合式索引:
- 含祖系和不等式篩選條件的查詢
- 使用一或多個不等式篩選條件查詢某個屬性,並使用一或多個等式查詢其他屬性的查詢
- 按遞減順序排序金鑰的查詢
- 採用多種排序順序的查詢
- 使用一或多個篩選條件且有一或多個排序順序的查詢
複合式索引必須在應用程式的索引設定檔 (index.yaml
) 中定義。(索引設定檔並未含有內建索引)。
複合式索引由多種屬性組成,不得將其中任何一個屬性排除於您的索引之外。
如要查看複合式索引,請前往 Google Cloud 控制台的「索引」頁面。您無法使用 Google Cloud 控制台建立或更新複合式索引。
若應用程式嘗試執行不能使用可用索引執行的查詢 (內建索引或是索引設定檔中指定的索引),查詢便會失敗。
Datastore mode API 會自動建議大多數應用程式適用的索引。依據應用程式的 Datastore 模式資料庫使用量,以及資料的大小與形狀而定,您或許可以手動調整索引。例如,寫入具有多個屬性值的實體,可能會形成爆炸式索引,導致儲存成本偏高,並拉長寫入延遲時間。
Datastore 模擬器可以讓您更容易管理索引設定檔。Datastore 模擬器可以產生能夠讓查詢成功的索引設定,不會因為要在沒有索引的情況下執行必須要有索引的查詢而失敗。如果您在本機測試應用程式時,會使用每一種篩選條件和排序順序組合執行應用程式可能會發出的每一個查詢,所產生的項目將代表一組完整的索引。如果您的測試不會執行每種可能的查詢形式,您可以在更新索引之前審查及調整索引設定檔。
如要進一步瞭解 index.yaml
,請參閱索引設定一文。
部署或刪除索引
索引設定檔修改完成後,請執行 gcloud datastore indexes create
指令讓索引設定開始生效。如要瞭解詳情,請參閱更新索引相關說明。
若不再需要之前部署過的索引,可以刪除未使用的索引。
儲存空間費用和寫入延遲
索引會產生儲存空間費用。索引項目大小說明內建索引和複合式索引如何影響資料庫的儲存空間大小。您可以透過 Firestore (Datastore 模式) 統計資料,查看索引項目和索引儲存空間大小的詳細資訊。
索引也會造成寫入延遲。更新屬性值時,資料庫也會更新所有相關索引。資料庫需要更新的索引越多,作業所需時間就越長。
您可以刪除未使用的索引,以及排除建立索引的屬性,藉此降低儲存空間費用並提升寫入效能。這也能避免作業因索引限制而失敗。
索引和屬性
關於索引以及索引與實體屬性之間的關聯性,請謹記以下幾項注意事項:
具有混合值類型的屬性
若兩個實體所含的屬性名稱相同但值類型不同,屬性索引會先按值類型排序實體,再按每種類型適合的次要順序排序。舉例來說,如果兩個實體各有一個名為 age
的屬性,其中一個含有整數值,另一個含有字串值,則以 age
屬性排序時,無論屬性值本身為何,具有整數值的實體一律優先於具有字串值的實體。
如果是整數和浮點數,尤其要注意這一點,因為 Datastore 模式會將這兩種類型視為不同的類型。由於會先排序所有整數再排序所有浮點數,因此若屬性含整數值 38
,其排序順序就會在含浮點數值 37.5
的屬性之前。
排除的屬性
若您知道自己絕對不會需要篩選或排序某個特定屬性,可以將該屬性排除於索引之外,不讓 Datastore 模式資料庫保留該屬性的索引項目。這樣能夠減少索引項目需要的儲存空間大小,有利於降低執行應用程式的成本。這也能縮短寫入延遲時間。實體若含已排除的屬性,就會表現出與未設定屬性相同的行為:如使用已排除的屬性為查詢的篩選條件或排序順序,絕對不會與該屬性相符。
以下範例是從索引中排除 description
屬性:
C#
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore C# API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
Go
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore Go API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
Java
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore Java API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
Node.js
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore Node.js API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
PHP
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore PHP API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
Python
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore Python API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
Ruby
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore Ruby API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
GQL
不適用如果排除 description
屬性,以下範例中的查詢將不會傳回任何結果:
C#
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore C# API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
Go
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore Go API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
Java
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore Java API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
Node.js
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore Node.js API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
PHP
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore PHP API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
Python
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore Python API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
Ruby
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore Ruby API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
GQL
# Will not return any results! SELECT * FROM Task WHERE description = 'A task description.'
之後您可以將屬性改回已編入索引。
然而請注意,若將屬性從已排除屬性改為已編入索引屬性,並不會影響改變之前建立的任何既有實體。查詢使用該屬性進行篩選時,將不會傳回這類現有實體,因為這些實體在建立時並未寫入查詢的索引。您必須將實體重新寫入資料庫,將其編入適當的索引中,未來的查詢才能使用這些實體。亦即,您必須為每個現有實體執行以下動作:
- 查詢 (get) 實體。
- 將實體寫入 (put) 回資料庫。
同樣地,若將已編入索引的屬性變更為已排除的屬性,只會影響之後寫入資料庫的實體。任何具有該屬性的既有實體,其索引項目仍然會繼續存在,直到更新或刪除該實體為止。為避免出現不必要的結果,您必須清除所有依 (已排除的) 屬性篩選或排序之查詢的程式碼。
索引限制
針對可以與單一實體相關聯的索引項目,Firestore (Datastore 模式) 會限制數量和總體大小。這些限制相當寬鬆,大多數應用程式都不受影響。不過,有些情況下您可能會受到限制。
如前文所述,Datastore 模式資料庫會在每個實體各屬性的預先定義索引中建立一個項目,但不含您已明確宣告為排除於索引之外的屬性。該屬性也可能列於索引設定檔 (index.yaml
) 中宣告的外加自訂索引中。如果實體並無任何清單屬性,則這類自訂索引 (非祖系索引) 最多各會有一個項目,如為祖系索引,則該實體的每個祖系會各有一個項目。每次屬性時出現變化時,皆須更新其中每一個索引項目。
若每個實體的某個屬性只有單一值,則每個實體只能將每個可能的值儲存在其預先定義索引中一次。即便如此,如實體包含大量此類單一值屬性,仍有可能會超過索引項目或大小限制。同樣地,若實體可以包含同一個屬性的多個值,每個值必須各有一個索引項目;而若可能值的數字太大,實體就有可能超過項目限制。
如果實體含有多個屬性,而每個屬性都有多個值,則情況會變得更嚴重。索引須針對每一個可能的屬性值「組合」編入一個項目,才能包含此類實體。如果自訂索引參照多個屬性,且每個屬性各含多個值,其組合可能會形成「爆炸式索引」,一個實體必須要含大量項目,而可能的屬性值數目則相對較少。此類「爆炸式索引」可能會大幅增加實體的儲存空間大小,因為必須儲存大量索引項目。爆炸式索引也可能導致實體超出索引項目數目或大小限制。
請考慮使用以下程式碼:
C#
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore C# API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
Go
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore Go API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
Java
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore Java API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
Node.js
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore Node.js API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
PHP
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore PHP API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
Python
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore Python API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
Ruby
如要瞭解如何安裝及使用 Cloud Datastore 的用戶端程式庫,請參閱「Cloud Datastore 用戶端程式庫」一文。詳情請參閱 Cloud Datastore Ruby API 參考說明文件。
如要向 Cloud Datastore 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。
GQL
不適用會建立一個 Task
實體,其中包含三個 tags
屬性值、三個 collaborators
屬性值,created
則設為目前日期。這樣就需要 9 個索引項目 (每個可能的屬性值組合各一個):
('fun'
, 'alice'
, NOW()
)
('fun'
, 'bob'
, NOW()
)
('fun'
, 'charlie'
, NOW()
)
('programming'
, 'alice'
, NOW()
)
('programming'
, 'bob'
, NOW()
)
('programming'
, 'charlie'
, NOW()
)
('learn'
, 'alice'
, NOW()
)
('learn'
, 'bob'
, NOW()
)
('learn'
, 'charlie'
, NOW()
)
多次重複同一個屬性後,Datastore 模式的 Firestore 可能會偵測到爆炸式索引並建議替代索引。然而,在所有其他情況下 (例如本範例定義的查詢),Datastore 模式資料庫均會產生爆炸式索引。在此情況下,您可以在索引設定檔中手動設定索引,避免出現爆炸式索引:
indexes:
- kind: Task
properties:
- name: tags
- name: created
- kind: Task
properties:
- name: collaborators
- name: created
這樣會將需要的項目減少到只有 (|tags|
*
|created|
+
|collaborators|
*
|created|)
或 6 個項目,而非 9 個:
('fun'
, NOW()
)
('programming'
, NOW()
)
('learn'
, NOW()
)
('alice'
, NOW()
)
('bob'
, NOW()
)
('charlie'
, NOW()
)
任何會導致索引超過索引項目或大小限制的 commit
作業都會失敗。錯誤文字會說明超過的限制 ("Too many indexed properties"
或 "Index entries too large"
),以及原因是哪一個自訂索引。如果新索引在建立時會超過任何實體的限制,查詢該索引就會失敗,而該索引也會在 Google Cloud 控制台中顯示為 Error
狀態。如何處理這類 Error
索引:
- 從索引設定檔 (
index.yaml
) 移除該索引。 - 使用 Google Cloud CLI,透過
datastore indexes cleanup
指令從資料庫移除索引,詳情請參閱「刪除未使用的索引」。 - 執行以下其中一項:
- 重新制定索引定義及對應的查詢,或者
- 移除會導致索引爆炸的實體。
- 將索引加回
index.yaml
。 - 使用 Google Cloud CLI 執行
datastore indexes create
指令,將索引新增至資料庫,詳情請參閱「更新索引」。
若要避免爆炸式索引,可以使用清單屬性避開需要自訂索引的查詢。如先前所述,這包括含多種排序順序的查詢,或是混用等式及不等式篩選條件的查詢。
投影索引
投影查詢要求必須將投影指定的屬性全數納入索引。Datastore 模擬器會在索引設定檔 index.yaml
(這是使用您的應用程式上傳的檔案) 中自動產生需要的索引。
如要盡可能減少必要的索引數目,其中一種方法是一致投影相同的屬性 (即使並非每一次都需要其中所有屬性)。例如,以下查詢需要兩個獨立的索引:
SELECT priority, percent_complete FROM Task
SELECT priority, percent_complete, created FROM Task
不過,若您一律投影 priority
、percent_complete
和 created
等屬性 (即使是在不需要 created
的情況下),則只需要一個索引。
如欲將現有查詢轉換為投影查詢,若查詢的其他部分尚不包含投影中的屬性,則可能必須建構一個新的索引。例如,假設有一個如下所示的現有查詢:
SELECT * FROM Task
WHERE priority > 1
ORDER BY priority, percent_complete
這項查詢需要以下索引:
indexes:
- kind: Task
properties:
- name: priority
- name: percent_complete
如果將這項索引轉換成以下任一個投影查詢:
SELECT created FROM Task
WHERE priority > 1
ORDER BY priority, percent_complete
SELECT priority, percent_complete, created FROM Task
WHERE priority > 1
ORDER BY priority, percent_complete
就會出現一個新的屬性 (created
),因而必須建立新索引:
indexes:
- kind: Task
properties:
- name: priority
- name: percent_complete
- name: created
但
SELECT priority, percent_complete FROM Task
WHERE priority > 1
ORDER BY priority, percent_complete
「不會」變更需要的索引,因為現有查詢中已包含投影屬性 priority
和 percent_complete
。
多個資料庫
您可以透過 gcloud firestore
管理 Datastore 模式的單一索引,也可以搭配 index.yaml 檔案使用 gcloud datastore
,管理資料庫中的所有索引。
gcloud firestore
gcloud firestore indexes composite create --api-scope=datastore-mode-api --query-scope=QUERY_SCOPE --database=DATABASE_ID
gcloud datastore
gcloud alpha datastore indexes create index.yaml --database=DATABASE_ID
更改下列內容:
- DATABASE_ID:資料庫 ID。
- QUERY_SCOPE:祖先索引為
collection-recursive
,非祖先索引為collection-group
。