Azure Cosmos DB 中的索引編製原則

適用於:NoSQL

在 Azure Cosmos DB 中,每個容器都有索引編製原則,以指示容器項目應該如何編制索引。 新建立的容器所套用之每個項目的每個屬性之預設索引編製原則,會對任何字串或數字強制執行範圍索引。 這可讓查詢的效能良好,而無須事先考慮索引編製和索引管理。

在某些情況下,您可以覆寫此自動行為,使其更符合您的需求。 您可設定索引編製模式來自訂容器的索引編製原則,並包含或排除屬性路徑

注意

本文所述的索引編製原則更新方法僅適用於 Azure Cosmos DB API for NoSQL。 深入了解適用於 MongoDB 的 Azure Cosmos DB API 中的索引編製

編製索引模式

Azure Cosmos DB 支援兩種索引編製模式:

  • 一致:當您建立、更新或刪除項目時,索引會同步更新。 意即讀取查詢的一致性將會是該帳戶所設定的一致性
  • :容器已停用索引編製。 此模式通常用於當容器作為單純的索引鍵值存放區,而不需要次要索引時。 此模式也可用於改善大量作業的效能。 完成大量作業之後,索引模式可設為一致,接著使用 IndexTransformationProgress來監視至完成為止。

注意

Azure Cosmos DB 同時支援延遲索引模式。 當引擎並未執行任何其他工作時,延遲索引會以較低的優先順序層級對更新執行索引。 這可能會導致不一致或不完整的查詢結果。 如果您打算查詢 Azure Cosmos DB 容器,則不應該選取延遲索引。 新容器無法選取延遲索引。 您可連絡 cosmosdbindexing@microsoft.com 要求豁免 (除非以不支援延遲索引的無伺服器模式使用 Azure Cosmos DB 帳戶)。

依預設,編製索引原則設為 automatic。 將索引編製原則中的 automatic 屬性設為 true 即可。 將此屬性設為 true,撰寫項目時 Azure Cosmos DB 便可自動編製索引。

索引大小

在 Azure Cosmos DB 中,已使用的總儲存體空間為資料大小和索引大小的加總。 以下幾項為索引大小相關功能:

  • 索引大小取決於索引編製原則。 若所有屬性皆已編製索引,則索引大小可能大於資料大小。
  • 刪除資料時,索引會以幾乎連續的方式進行壓縮。 但刪除小型資料時,可能不會立即觀察到索引大小有所縮減。
  • 分割實體分割區時,索引大小可能會暫時增加。 分割區分割完成後,便會釋放索引空間。

包含和排除屬性路徑

自訂索引編製原則可指定索引編製時要明確包含或排除的屬性路徑。 透過最佳化已編製索引的路徑數目,便可大幅降低寫入作業的延遲和 RU 費用。 這些路徑會依索引編製概觀一節所述方法定義,並新增下列部分:

  • 導向純量值 (字串或數字) 的路徑結尾為 /?
  • 陣列的元素使用 /[] 標記法 (而不是 /0/1 等方式) 一併處理
  • /* 萬用字元可用於比對節點下的任何元素

再舉相同範例:

    {
        "locations": [
            { "country": "Germany", "city": "Berlin" },
            { "country": "France", "city": "Paris" }
        ],
        "headquarters": { "country": "Belgium", "employees": 250 },
        "exports": [
            { "city": "Moscow" },
            { "city": "Athens" }
        ]
    }
  • headquartersemployees 路徑為 /headquarters/employees/?

  • locationscountry 路徑為 /locations/[]/country/?

  • headquarters 下的任何路徑皆為 /headquarters/*

例如,我們可包含 /headquarters/employees/? 路徑。 此路徑可確保編製員工屬性的索引,但不會在此屬性內編製額外的巢狀 JSON 索引。

包含/排除策略

所有索引編製原則皆須包含根路徑 /*,作為包含或排除的路徑。

  • 包含根路徑時,即可選擇性排除不需要編製索引的路徑。 此為建議作法,如此 Azure Cosmos DB 便可主動為可能新增至模型的新屬性編製索引。

  • 排除根路徑時,即可選擇性包含需要編製索引的路徑。 根據預設,分割區索引鍵屬性路徑不會透過排除策略來編製索引,而且應該視需要明確包含。

  • 針對具有一般字元 (含英數位元和 _ (底線) 的路徑),無須使用雙引號逸出路徑字串 (例如 "/path/?")。 針對具有其他特殊字元的路徑,則須使用雙引號逸出路徑字串 (例如 "/" path-abc "/?")。 若預期路徑會有特殊字元,保險起見可逸出每個路徑。 無論是逸出每個路徑,或只逸出具特殊字元的路徑,功能上皆無差異。

  • 除非將 Etag 新增至包含的路徑進行索引編製,否則預設將排除系統屬性 _etag

  • 若索引編製模式設為一致,便會自動為系統屬性 id_ts 編製索引。

  • 如果項目中沒有明確編製索引的路徑,則會在索引中新增值來表示路徑未定義。

所有明確包含的路徑都會在容器中每個項目的索引中新增值,即使指定項目未定義路徑也一樣。

請參閱這一節,查看包含和排除路徑的編製索引原則範例。

包含/排除優先順序

若包含的路徑和排除的路徑有所衝突,則優先採用較為精確的路徑。

以下是範例:

包含的路徑/food/ingredients/nutrition/*

排除的路徑/food/ingredients/*

在此情況下,由於包含的路徑較為精確,因此優先順序高於排除的路徑。 根據這些路徑,索引會排除 food/ingredients 路徑或巢狀內的所有資料。 包含的路徑 /food/ingredients/nutrition/* 內資料則為例外,其會編製索引。

以下為 Azure Cosmos DB 中包含和排除路徑優先順序的幾項規則:

  • 多層路徑比少層路徑更精確。 例如:/a/b/?/a/? 更精確。

  • /?/* 更精確。 例如 /a/?/a/* 更準確,因此 /a/? 優先度較高。

  • 此路徑 /* 須為包含的路徑或排除的路徑。

空間索引

在索引編製原則中定義空間路徑時,須定義應套用至該路徑的索引 type。 空間索引的可能類型包括:

  • Point

  • Polygon

  • MultiPolygon

  • LineString

依預設,Azure Cosmos DB 不會建立任何空間索引。 若要使用空間 SQL 內建函式,則應建立所需屬性的空間索引。 請參閱此節,查看新增空間索引的索引編製原則範例。

複合索引

若查詢的子句 ORDER BY 具有兩個以上的屬性,便需要複合式索引。 您也可定義複合式索引,以改善許多相等和範圍查詢的效能。 依預設,系統不會定義任何複合式索引,因此您應視需要新增複合式索引

不同於包含或排除的路徑,您無法使用 /* 萬用字元來建立路徑。 在無須指定的各複合路徑結尾,皆有隱含的 /?。 複合路徑會導向純量值,且此為複合式索引中包含的唯一值。 如果複合式索引中的路徑不存在於項目中或導致非純量值,則索引中會新增值來指出路徑未定義。

定義複合式索引時可指定:

  • 兩個以上的屬性路徑。 定義屬性路徑的序列很重要。

  • 順序 (遞增或遞減)。

注意

新增複合式索引時,查詢會使用現有的範圍索引,直到新的複合式索引完成新增為止。 因此新增複合索引時,可能不會立即觀察到效能有所改善。 您可使用其中一個 SDK 來追蹤索引轉換的進度。

多個屬性的 ORDER BY 查詢:

若查詢包含的子句 ORDER BY 具有兩個以上的屬性,使用複合式索引時則有下列考量:

  • 若複合式索引路徑與 ORDER BY 子句中的屬性序列不相符,複合式索引便無法支援查詢。

  • 複合式索引路徑的順序 (遞增或遞減) 也應符合 ORDER BY 子句中的 order

  • 複合式索引也支援 ORDER BY 子句順序相反的所有路徑。

請見下列範例,針對名稱、年齡和 _ts 等屬性定義複合式索引:

複合式索引 範例ORDER BY查詢 複合式索引可支援嗎?
(name ASC, age ASC) SELECT * FROM c ORDER BY c.name ASC, c.age asc Yes
(name ASC, age ASC) SELECT * FROM c ORDER BY c.age ASC, c.name asc No
(name ASC, age ASC) SELECT * FROM c ORDER BY c.name DESC, c.age DESC Yes
(name ASC, age ASC) SELECT * FROM c ORDER BY c.name ASC, c.age DESC No
(name ASC, age ASC, timestamp ASC) SELECT * FROM c ORDER BY c.name ASC, c.age ASC, timestamp ASC Yes
(name ASC, age ASC, timestamp ASC) SELECT * FROM c ORDER BY c.name ASC, c.age ASC No

您應自訂索引編製原則,以便提供所有必要的 ORDER BY 查詢。

具有多屬性篩選條件的查詢

若查詢有兩個以上屬性的篩選條件,為這些屬性建立複合式索引則可能有所幫助。

例如,請見下列同時使用相等和範圍篩選條件的查詢:

SELECT *
FROM c
WHERE c.name = "John" AND c.age > 18

若能運用 (name ASC, age ASC) 的複合式索引,此查詢將可改善效率、縮短時間,且耗用更少 RU。

使用多個範圍篩選條件的查詢也可透過複合式索引最佳化。 但每個個別的複合式索引僅可最佳化單一範圍篩選條件。 範圍篩選包括 ><<=>=!=。 在複合式索引中,範圍篩選條件應於最後定義。

請見下列查詢,使用一個相等篩選條件和兩個範圍篩選條件:

SELECT *
FROM c
WHERE c.name = "John" AND c.age > 18 AND c._ts > 1612212188

(name ASC, age ASC)(name ASC, _ts ASC) 使用複合式索引時,此查詢將更有效率。 但該查詢的 (age ASC, name ASC) 不會使用複合式索引,因為使用相等篩選條件的屬性必須先在複合式索引中定義。 由於各複合式索引僅可最佳化一個範圍篩選條件,因此 (name ASC, age ASC, _ts ASC) 必須使用兩個不同的複合式索引,而不是一個複合式索引。

針對使用多個屬性篩選條件的查詢建立複合式索引時,適用下列考量

  • 篩選條件運算式可使用多個複合式索引。
  • 查詢篩選條件中的屬性應符合複合式索引中的屬性。 若複合式索引包含某屬性,但查詢未使用該屬性作為篩選條件,則查詢將不會使用複合式索引。
  • 若查詢的篩選條件中有其他屬性未於複合式索引中定義,則會使用複合式索引和範圍索引的組合來評估該查詢。 如此所需的 RU 會少於單獨使用範圍索引的情況。
  • 若屬性具有範圍篩選條件 (><<=>=!=),則在複合式索引中應於最後定義此屬性。 若查詢有一個以上的範圍篩選條件,多個複合式索引可能有所助益。
  • 建立複合式索引來最佳化包含多個篩選準則的查詢時,複合式索引的 ORDER 不會影響結果。 這個屬性為選擇性。

請見下列範例,針對名稱、年齡和時間戳記等屬性定義複合式索引:

複合式索引 範例查詢 複合式索引可支援嗎?
(name ASC, age ASC) SELECT * FROM c WHERE c.name = "John" AND c.age = 18 Yes
(name ASC, age ASC) SELECT * FROM c WHERE c.name = "John" AND c.age > 18 Yes
(name ASC, age ASC) SELECT COUNT(1) FROM c WHERE c.name = "John" AND c.age > 18 Yes
(name DESC, age ASC) SELECT * FROM c WHERE c.name = "John" AND c.age > 18 Yes
(name ASC, age ASC) SELECT * FROM c WHERE c.name != "John" AND c.age > 18 No
(name ASC, age ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" AND c.age = 18 AND c.timestamp > 123049923 Yes
(name ASC, age ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" AND c.age < 18 AND c.timestamp = 123049923 No
(name ASC, age ASC) and (name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" AND c.age < 18 AND c.timestamp > 123049923 Yes

使用篩選和 ORDER BY 的查詢

若查詢使用一或多個屬性進行篩選,且 ORDER BY 子句中有不同屬性,將這些屬性加入 ORDER BY 子句便可能有所幫助。

例如,可將篩選的屬性加入 ORDER BY 子句,運用複合式索引重新撰寫下列查詢:

使用範圍索引的查詢:

SELECT *
FROM c 
WHERE c.name = "John" 
ORDER BY c.timestamp

使用複合式索引的查詢:

SELECT * 
FROM c 
WHERE c.name = "John"
ORDER BY c.name, c.timestamp

所有具有篩選條件的 ORDER BY 查詢皆適用於相同的查詢最佳化;請注意,個別複合式索引最多僅可支援一個範圍篩選條件。

使用範圍索引的查詢:

SELECT * 
FROM c 
WHERE c.name = "John" AND c.age = 18 AND c.timestamp > 1611947901 
ORDER BY c.timestamp

使用複合式索引的查詢:

SELECT * 
FROM c 
WHERE c.name = "John" AND c.age = 18 AND c.timestamp > 1611947901 
ORDER BY c.name, c.age, c.timestamp

此外,您也可使用複合式索引,以系統函式和 ORDER BY 來最佳化查詢:

使用範圍索引的查詢:

SELECT * 
FROM c 
WHERE c.firstName = "John" AND Contains(c.lastName, "Smith", true) 
ORDER BY c.lastName

使用複合式索引的查詢:

SELECT * 
FROM c 
WHERE c.firstName = "John" AND Contains(c.lastName, "Smith", true) 
ORDER BY c.firstName, c.lastName

使用篩選和 ORDER BY 子句建立複合式索引、最佳化查詢時,適用下列考量:

  • 若含某屬性篩選條件的查詢未定義複合式索引,且有其他 ORDER BY 子句使用不同屬性,該查詢仍會成功。 但使用複合式索引時可降低查詢的 RU 成本,特別是當子句 ORDER BY 中的屬性具有高基數時。
  • 若查詢使用屬性來進行篩選,則 ORDER BY 子句中應先包含這些屬性。
  • 若查詢使用多個屬性來進行篩選,則相等篩選條件須為 ORDER BY 子句中的第一組屬性。
  • 若查詢使用多個屬性來進行篩選,各複合式索引最多可使用一個範圍篩選條件或系統函式。 範圍篩選條件或系統函式中所用的屬性,應於複合式索引中最後定義。
  • 使用多個屬性進行篩選的查詢、針對 ORDER BY 查詢建立複合式索引的所有考量仍適用。
複合式索引 範例ORDER BY查詢 複合式索引可支援嗎?
(name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" ORDER BY c.name ASC, c.timestamp ASC Yes
(name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" AND c.timestamp > 1589840355 ORDER BY c.name ASC, c.timestamp ASC Yes
(timestamp ASC, name ASC) SELECT * FROM c WHERE c.timestamp > 1589840355 AND c.name = "John" ORDER BY c.timestamp ASC, c.name ASC No
(name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" ORDER BY c.timestamp ASC, c.name ASC No
(name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" ORDER BY c.timestamp ASC No
(age ASC, name ASC, timestamp ASC) SELECT * FROM c WHERE c.age = 18 and c.name = "John" ORDER BY c.age ASC, c.name ASC,c.timestamp ASC Yes
(age ASC, name ASC, timestamp ASC) SELECT * FROM c WHERE c.age = 18 and c.name = "John" ORDER BY c.timestamp ASC No

使用篩選和彙總的查詢

若查詢使用一或多個屬性進行篩選,且使用彙總系統函式,在篩選條件和彙總系統函式中建立這些屬性的複合式索引便可能有所幫助。 此最佳化適用於 SUMAVG 系統函式。

透過篩選和彙總系統函式最佳化查詢時,適用下列考量。

  • 使用彙總執行查詢時可選用複合式索引。 但使用複合式索引時,查詢的 RU 成本通常可大幅減少。
  • 若查詢使用多個屬性來進行篩選,則相等篩選條件須為複合式索引中的第一組屬性。
  • 各複合式索引最多可有一個範圍篩選條件,且須為彙總系統函式的屬性。
  • 在複合式索引中,彙總系統函式中的屬性應於最後定義。
  • order (ASCDESC) 並不重要。
複合式索引 範例查詢 複合式索引可支援嗎?
(name ASC, timestamp ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name = "John" Yes
(timestamp ASC, name ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name = "John" No
(name ASC, timestamp ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name > "John" No
(name ASC, age ASC, timestamp ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name = "John" AND c.age = 25 Yes
(age ASC, timestamp ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name = "John" AND c.age > 25 No

修改編製索引原則

您可使用 Azure 入口網站或其中一個支援的 SDK,隨時更新容器的索引編製原則。 更新索引編製原則時,即會觸發將舊索引轉換為新索引;此作業於線上和就地執行 (因此作業期間不會耗用額外的儲存空間)。 舊的索引編製原則會有效轉換為新原則,而不會影響寫入可用性、讀取可用性或容器所佈建的輸送量。 索引轉換是非同步作業,完成所需的時間取決於佈建的輸送量、項目數及大小。 如果必須進行多個編製索引原則更新,建議您將所有變更當做單一作業來執行,以便儘快完成索引轉換。

重要

索引轉換作業會耗用要求單位。 若使用無伺服器容器,索引轉換所耗用的要求單位目前不計費。 這些要求單位將於無伺服器正式推出後開始計費。

注意

您可在 Azure 入口網站中或使用其中一個 SDK 來追蹤索引轉換的進度。

任何索引轉換期間皆不會影響寫入可用性。 索引轉換會使用您佈建的 RU,但優先順序低於 CRUD 作業或查詢。

新增索引路徑時不會影響讀取可用性。 在索引轉換完成後,查詢才會使用新的索引路徑。 意即新增索引路徑時,若查詢採用該索引路徑,索引轉換之前及期間的效能皆相同。 索引轉換完成後,查詢引擎便會開始使用新的索引路徑。

移除索引路徑時,應將所有變更一次納入索引編製原則轉換作業。 若在一次索引編製原則變更中移除多個索引,在整個索引轉換期間查詢引擎會提供一致且完整的結果。 但若透過多次索引編製原則變更來移除索引,則在所有索引轉換完成前,查詢引擎不會提供一致或完整的結果。 開發人員大多不會在卸除索引後,隨即又嘗試運用這些索引執行查詢,因此實務上這種情況發生的可能性不高。

卸除索引路徑時,查詢引擎將立即停止使用該路徑,改為進行完整掃描。

注意

如可能,應一律嘗試將多項索引移除納入單一項索引編製原則修改作業。

重要

移除索引會立即生效,而新增索引需要一些時間,因為需要索引轉換。 將一個索引取代為另一個索引時 (例如,將單一屬性索引取代為複合式索引),請務必先新增新的索引,然後等候索引轉換完成,從索引編製原則中移除先前的索引。 否則,這會對查詢上一個索引的能力造成負面影響,並可能會中斷任何參考上一個索引的作用中工作負載。

索引編製原則和 TTL

使用存留時間 (TTL) 功能時需要編製索引。 這表示:

  • 索引編製模式設為 none 的容器無法啟用 TTL,
  • 啟用 TTL 的容器無法將索引編製模式設為 [無]。

若沒有需要編製索引的屬性路徑,但需要使用 TTL,則可使用索引編製原則,並將索引編製模式設為 consistent、無包含的路徑,且以 /* 作為唯一排除的路徑。

下一步

在下列文章中深入了解編製索引: