SELECT *
FROM c
WHERE c.description = "Malabar spinach, cooked"
索引策略:
"indexingMode": "consistent",
"automatic": true,
"includedPaths": [
"path": "/*"
"excludedPaths": [
"path": "/description/*"
RU 费用:409.51 RU
更新的索引策略:
"indexingMode": "consistent",
"automatic": true,
"includedPaths": [
"path": "/*"
"excludedPaths": []
RU 费用:2.98 RU
可以随时将属性添加到索引策略,而不会影响写入或读取可用性。 你可以跟踪索引转换进度。
了解哪些系统函数使用索引
大多数系统函数都使用索引。 下面列出了一些使用索引的常用字符串函数:
-StartsWith
RegexMatch
Substring - 但是仅当在第一个 num_expr 为 0 时
下面是一些在 WHERE 子句中使用时不使用索引且必须加载每个文档的常用系统函数:
Upper/Lower
不要使用系统函数来规范化数据以进行比较,而是在插入时规范化大小写。 诸如 SELECT * FROM c WHERE UPPER(c.name) = 'BOB' 的查询将变成 SELECT * FROM c WHERE c.name = 'BOB'。
GetCurrentDateTime/GetCurrentTimestamp/GetCurrentTicks
计算查询执行前的当前时间并在 WHERE 子句中使用该字符串值。
数学函数(非聚合)
如果需要频繁计算查询中的某个值,请考虑在 JSON 文档中将此值存储为属性。
这些系统函数可以使用索引,但在包含聚合的查询中使用时除外:
在 SELECT 子句中使用时,低效的系统函数不会影响查询使用索引的方式。
改进字符串系统函数执行
对于某些使用索引的系统函数,可以通过将 ORDER BY 子句添加到查询来改进查询执行。
更具体地说,在查询中添加 ORDER BY 时,RU 费用随属性的基数增加而增加的任何系统函数都可能从中受益。 这些查询进行索引扫描,因此,对查询结果进行排序可以提高查询的效率。
此优化可改进以下系统函数的执行:
StartsWith(其中不区分大小写 = true)
StringEquals(其中不区分大小写 = true)
RegexMatch
EndsWith
例如,考虑下面带有 CONTAINS 的查询。 CONTAINS 将使用索引,但是有时候,即使在添加相关索引后,在运行以下查询时,仍然会看到非常高的 RU 费用:
原始查询:
SELECT *
FROM c
WHERE CONTAINS(c.town, "Sea")
可以通过添加 ORDER BY 来改进查询执行:
SELECT *
FROM c
WHERE CONTAINS(c.town, "Sea")
ORDER BY c.town
相同的优化可帮助对其他筛选器进行查询。 在这种情况下,最好还向 ORDER BY 子句添加具有相等筛选器的属性。
原始查询:
SELECT *
FROM c
WHERE c.name = "Samer" AND CONTAINS(c.town, "Sea")
可以通过添加 ORDER BY 和 (c.name, c.town) 的组合索引来改进查询执行:
SELECT *
FROM c
WHERE c.name = "Samer" AND CONTAINS(c.town, "Sea")
ORDER BY c.name, c.town
了解哪些聚合查询使用索引
在大多数情况下,Azure Cosmos DB 中的聚合系统函数将使用索引。 但是,根据聚合查询中的筛选器或其他子句,可能需要查询引擎来加载大量文档。 通常情况下,查询引擎将首先应用相等性和范围筛选器。 应用这些筛选器后,查询引擎可以评估其他筛选器,并根据需要加载其余文档以计算聚合。
例如,给定以下两个示例查询,同时具有相等性和 CONTAINS 系统函数筛选器的查询总体上要比只具有 CONTAINS 系统函数筛选器的查询更加高效。 这是因为在需要为费用更高的 CONTAINS 筛选器加载文档之前,首先应用了相等性筛选器且相等性筛选器使用了索引。
仅具有 CONTAINS 筛选器的查询 - 较高的 RU 费用:
SELECT COUNT(1)
FROM c
WHERE CONTAINS(c.description, "spinach")
同时具有相等性筛选器和 CONTAINS 筛选器的查询 - 较低的 RU 费用:
SELECT AVG(c._ts)
FROM c
WHERE c.foodGroup = "Sausages and Luncheon Meats" AND CONTAINS(c.description, "spinach")
下面是不会完全使用索引的聚合查询的其他示例:
具有系统函数且不使用索引的查询
应参阅相关系统函数页以查看其是否使用索引。
SELECT MAX(c._ts)
FROM c
WHERE CONTAINS(c.description, "spinach")
具有用户定义函数 (UDF) 的聚合查询
SELECT AVG(c._ts)
FROM c
WHERE udf.MyUDF("Sausages and Luncheon Meats")
具有 GROUP BY 的查询
随着 GROUP BY 子句中属性基数的增加,具有 GROUP BY 的查询的 RU 费用也将增加。 例如,在下面的查询中,查询的 RU 费用将随着数值唯一描述的增加而增加。
具有 GROUP BY 子句的聚合函数的 RU 费用将高于单独的聚合函数的 RU 费用。 在此示例中,查询引擎必须加载与 c.foodGroup = "Sausages and Luncheon Meats" 筛选器匹配的每个文档,因此 RU 费用预计会很高。
SELECT COUNT(1)
FROM c
WHERE c.foodGroup = "Sausages and Luncheon Meats"
GROUP BY c.description
如果计划频繁运行相同的聚合查询,则与运行单个查询相比,使用 Azure Cosmos DB 更改源生成实时具体化视图可能更加高效。
优化同时具有筛选器和 ORDER BY 子句的查询
虽然具有筛选器和 ORDER BY 子句的查询通常使用范围索引,但如果能够通过组合索引提供这些查询,这些查询将会更加高效。 除了修改索引策略以外,还应将组合索引中的所有属性添加到 ORDER BY 子句。 对查询进行的此更改可确保该查询使用组合索引。 可以通过对 nutrition 数据集运行查询来观察影响:
SELECT *
FROM c
WHERE c.foodGroup = "Soups, Sauces, and Gravies"
ORDER BY c._ts ASC
索引策略:
"automatic":true,
"indexingMode":"Consistent",
"includedPaths":[
"path":"/*"
"excludedPaths":[]
RU 费用:44.28 RU
更新的查询(包含 ORDER BY 子句中的两个属性):
SELECT *
FROM c
WHERE c.foodGroup = "Soups, Sauces, and Gravies"
ORDER BY c.foodGroup, c._ts ASC
更新的索引策略:
"automatic":true,
"indexingMode":"Consistent",
"includedPaths":[
"path":"/*"
"excludedPaths":[],
"compositeIndexes":[
"path":"/foodGroup",
"order":"ascending"
"path":"/_ts",
"order":"ascending"
RU 费用:8.86 RU
使用子查询优化 JOIN 表达式
多值子查询可以通过在 WHERE 子句中的每个 select-many 表达式后面(而不是所有交叉联接后面)推送谓词,来优化 JOIN 表达式。
请看下面的查询:
SELECT Count(1) AS Count
FROM c
JOIN t IN c.tags
JOIN n IN c.nutrients
JOIN s IN c.servings
WHERE t.name = 'infant formula' AND (n.nutritionValue > 0
AND n.nutritionValue < 10) AND s.amount > 1
RU 费用:167.62 RU
对于此查询,索引将匹配包含名为 infant formula、nutritionValue 大于 0 且 amount 大于 1 的标记的任何文档。 此处的 JOIN 表达式将在应用任何筛选器之前,对每个匹配文档执行 tags、nutrients 和 servings 数组的所有项的叉积计算。 然后,WHERE 子句将对每个 <c, t, n, s> 元组应用筛选谓词。
例如,如果匹配文档在这三个数组中的每个数组中都具有 10 个项,则该文档将扩展为 1 x 10 x 10 x 10(即 1000)个元组。 在此处使用子查询可帮助在与下一个表达式联接之前,筛选出联接的数组项。
此查询等效于前面的查询,但使用了子查询:
SELECT Count(1) AS Count
FROM c
JOIN (SELECT VALUE t FROM t IN c.tags WHERE t.name = 'infant formula')
JOIN (SELECT VALUE n FROM n IN c.nutrients WHERE n.nutritionValue > 0 AND n.nutritionValue < 10)
JOIN (SELECT VALUE s FROM s IN c.servings WHERE s.amount > 1)
RU 费用:22.17 RU
假设 tags 数组中只有一个项与筛选器相匹配,而 nutrients 和 servings 数组各有 5 个项。 那么,JOIN 表达式将扩展为 1 x 1 x 5 x 5 = 25 个项,而不是第一个查询中的 1000 个项。
“已检索文档计数”等于“输出文档计数”的查询
如果“已检索文档计数”约等于“输出文档计数”,则查询引擎无需扫描许多不必要的文档 。 对于许多查询(例如,使用 TOP 关键字的查询)而言,“已检索文档计数”可能要比“输出文档计数”多 1 。 你无需为此担心。
最大限度地减少跨分区查询
当请求单位和数据存储需求增加时,Azure Cosmos DB 将使用分区来扩展单个容器。 每个物理分区具有不同的独立索引。 如果查询具有匹配容器分区键的相等性筛选器,则你只需检查相关分区的索引。 这种优化可以减少查询所需的 RU 总数。
如果有大量预配的 RU(超过 30,000)或存储了大量数据(约超过 100 GB),那么你可能需要有一个足够大的容器,以查看查询 RU 费用的显著降低。
例如,如果使用分区键 foodGroup 创建容器,则以下查询只需检查单个物理分区:
SELECT *
FROM c
WHERE c.foodGroup = "Soups, Sauces, and Gravies" and c.description = "Mushroom, oyster, raw"
具有带分区键的 IN 筛选器的查询将仅检查相关的物理分区,而不会“扇出”:
SELECT *
FROM c
WHERE c.foodGroup IN("Soups, Sauces, and Gravies", "Vegetables and Vegetable Products") and c.description = "Mushroom, oyster, raw"
对分区键使用范围筛选器或者对分区键不使用任何筛选器的查询将需要“扇出”,并检查每个物理分区的索引,以查看结果:
SELECT *
FROM c
WHERE c.description = "Mushroom, oyster, raw"
SELECT *
FROM c
WHERE c.foodGroup > "Soups, Sauces, and Gravies" and c.description = "Mushroom, oyster, raw"
优化对多个属性具有筛选器的查询
虽然对多个属性具有筛选器的查询通常使用范围索引,但如果能够通过组合索引提供这些查询,这些查询将会更加高效。 对于少量数据而言,这种优化不会产生明显影响。 但对于大量数据,它可能非常有用。 对于每个组合索引,最多只能优化一个非相等性筛选器。 如果查询具有多个非相等性筛选器,请选取其中一个将使用组合索引的筛选器。 其余筛选器将继续使用范围索引。 非相等性筛选器必须在组合索引中最后定义。 详细了解组合索引。
下面是一些可以通过组合索引进行优化的查询示例:
SELECT *
FROM c
WHERE c.foodGroup = "Vegetables and Vegetable Products" AND c._ts = 1575503264
SELECT *
FROM c
WHERE c.foodGroup = "Vegetables and Vegetable Products" AND c._ts > 1575503264
下面是相关的组合索引:
"automatic":true,
"indexingMode":"Consistent",
"includedPaths":[
"path":"/*"
"excludedPaths":[],
"compositeIndexes":[
"path":"/foodGroup",
"order":"ascending"
"path":"/_ts",
"order":"ascending"
可降低查询延迟的优化
在许多情况下,当查询延迟仍然过高时,RU 费用是可接受的。 以下部分概述了降低查询延迟的提示。 如果对同一个数据集多次运行同一个查询,该查询通常每次都会产生相同的 RU 开销。 但是,每次执行查询时,查询延迟可能各不相同。
提高邻近度
在非 Azure Cosmos DB 帐户的区域中运行的查询,比在同一区域中运行的查询的延迟更高。 例如,如果在台式机上运行代码,则延迟比从 Azure Cosmos DB 所在的同一 Azure 区域中的某个虚拟机上运行查询要高出几十或几百毫秒(或更高)。 可以轻松在 Azure Cosmos DB 中全局分发数据,以确保将数据置于更靠近应用的位置。
增加预配的吞吐量
在 Azure Cosmos DB 中,预配的吞吐量以请求单位 (RU) 计量。 假设某个查询消耗 5 RU 吞吐量。 如果预配 1000 RU,则每秒可以运行该查询 200 次。 如果在没有足够可用吞吐量的情况下尝试运行查询,Azure Cosmos DB 将返回 HTTP 429 错误。 任何当前 API for NoSQL SDK 在等待一小段时间后,都将自动重试该查询。 受限制的请求需要花费更长的时间,因此增加预配的吞吐量可以改进查询延迟。 可以在 Azure 门户的“指标”边栏选项卡上观察受限制的请求总数。
增加 MaxConcurrency
并行查询的方式是并行查询多个分区。 但就查询本身而言,会按顺序提取单个已分区集合中的数据。 因此,如果将 MaxConcurrency 设置为分区数,则最有可能实现查询的最高性能,但前提是所有其他系统条件仍保持不变。 如果不知道分区数,可将 MaxConcurrency(或旧 SDK 版本中的 MaxDegreesOfParallelism)设置为较大的数字。 系统会选择最小值(分区数、用户提供的输入)作为最大并行度。
增加 MaxBufferedItemCount
查询专用于在客户端处理当前一批结果时预提取结果。 预提取可帮助改进查询的总体延迟。 设置 MaxBufferedItemCount 会限制预提取结果的数目。 如果将此值设置为预期返回的结果数(或更大的数目),则查询从预提取中获益的程度将最大。 如果将此值设置为 -1,则系统将自动确定要缓冲的项数。
参阅以下文章,了解有关如何按查询度量 RU、获取执行统计信息以优化查询等信息:
使用 .NET SDK 获取 SQL 查询执行指标
优化 Azure Cosmos DB 的查询性能
.NET SDK 性能提示
适用于 Java v4 SDK 的性能提示