每个 Datastore 模式 Firestore 的查询都会使用一个或多个索引来计算结果,索引按索引属性指定的序列包含实体键和实体的祖先实体(可选)。如果应用对其实体进行了任何更改,索引会相应地更新,因此无需进一步计算即可提供所有查询的正确结果。
索引有两种类型:
- 内置索引
- 默认情况下,Datastore 模式数据库会自动为每个实体种类的每个属性预定义一个索引。这些单属性索引适用于简单类型的查询。
- 复合索引
- 复合索引根据每个已编入索引的实体的多个属性值编制索引。复合索引支持复杂查询,可在索引配置文件 (
index.yaml
) 中进行定义。
我们稍后会更详细地介绍索引的类型。
索引定义和结构
索引是在给定实体种类的属性列表上定义的,每个属性都具有相应的顺序(升序或降序)。为了与祖先查询一起使用,索引还可包含实体的祖先实体(可选)。
对于索引定义中指定的每个属性,索引都会包含一个相应的条目。索引中的每个条目都表示一个实体,即根据索引进行查询时,可能会出现在结果中的一个实体。只有当针对索引中使用的每个属性,实体都设置有索引值时,该实体才会包含在索引中;如果索引定义引用了实体没有值的属性,则该实体不会显示在索引中,因此永远不会作为基于该索引的任何查询的结果返回。
复合索引按先祖先实体再属性值的次序以索引定义中指定的顺序进行排序。有了这些了解,您就可以创建完美索引,以实现高效查询。
索引配置
Datastore 模式 Firestore 会为以下形式的查询提供内置(自动)索引:
- 仅使用祖先实体和键过滤条件的不分类查询
- 仅使用祖先实体和相等性过滤条件的查询
- 仅使用不等性过滤条件的查询(限于单个属性)
- 仅使用祖先实体过滤条件、属性等式过滤条件和键不等式过滤条件的查询
- 对属性不使用任何过滤条件且只要求遵循一种排序顺序(升序或降序)的查询
例如,默认情况下,Datastore 模式数据库会自动为每个实体种类的各属性预定义两个单属性索引:一个按升序排列,另一个按降序排列。如果您不希望数据库保留某个属性的索引,请从索引中排除该属性。请注意,排除某个属性会将其从所有复合索引中移除。
内置索引足以执行许多简单的查询,例如纯等式查询和简单的不等式查询。
内置索引不会显示在 Google Cloud 控制台的“索引”页面中。
对于更复杂的查询,应用必须定义复合或手动索引。以下形式的查询需使用复合索引:
- 具有祖先实体和不等式过滤条件的查询
- 对某属性设定了一个或多个不等式过滤条件,且对其他属性设定了一个或多个等式过滤条件的查询
- 具有基于键的降序排序顺序的查询
- 具有多个排序顺序的查询
- 具有一个或多个过滤条件和一个或多个排序顺序的查询
复合索引在应用的索引配置文件 (index.yaml
) 中定义。(索引配置文件中没有内置索引。)
复合索引由多个属性构成,并要求每个单独的属性都不得从索引中排除。
复合索引可在 Google Cloud 控制台的“索引”页面中查看。您无法使用 Google Cloud 控制台创建或更新复合索引。
如果应用尝试执行无法使用可用索引(无论是内置索引,还是索引配置文件中指定的索引)执行的查询,则该查询将失败。
Datastore 模式 API 会自动提供适合大多数应用的索引建议。您可能需要手动调整您的索引,具体取决于您的应用对 Datastore 模式数据库的使用情况以及数据的大小和形状。例如,编写具有多个属性值的实体可能会导致存储费用极高的爆炸式索引并增加写入延迟时间。
Datastore 模拟器可帮助您更轻松地管理索引配置文件。在执行需要索引但没有索引的查询时,Datastore 模拟器不会使查询失败,而是会生成一个索引配置,使查询成功。如果应用的本地测试作业执行应用将会发出的每个可能查询(使用过滤条件和排序顺序的每种组合),则生成的条目将构成一个完整的索引集。如果您的测试不执行每种可能的查询形式,则可在更新索引之前查看并调整索引配置文件。
如需详细了解 index.yaml
,请参阅索引配置。
部署或删除索引
索引配置文件修改完毕后,请运行 gcloud datastore indexes create
命令以将索引部署到服务中。如需了解详情,请参阅更新索引。
如果无需再使用之前部署的索引,可删除未使用的索引。
存储费用和写入延迟时间
索引会增加存储费用。索引条目大小介绍了内置索引和复合索引对数据库的存储空间大小的影响。您可以在 Datastore 模式 Firestore 统计信息中详细了解索引条目和索引存储大小。
索引还会影响写入延迟时间。更新属性值时,数据库还会更新每个相关索引。数据库需要更新的索引越多,操作所需的时间越长。
您可以通过删除未使用的索引和将属性从索引中排除来降低存储费用并提高写入性能。这还可以避免由于索引限制而导致操作失败。
索引和属性
下面是一些需牢记的特殊注意事项,它们针对的是索引及其与实体属性进行关联的方式:
具有混合值类型的属性
当两个实体具有名称相同但值类型不同的属性时,属性的索引首先按值类型排列实体,然后按照适合每种类型的次级排序排列实体。例如,如果两个实体都具有一个名为 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.'
您稍后可将该属性重新编入索引。
但请注意,将属性从“已排除”状态更改为“编入索引”不会影响此项更改前可能已创建的任何现有实体。针对该属性过滤的查询不会返回此类现有实体,因为这些实体在创建时未写入查询的索引。要让实体可供将来的查询使用,您必须将其重新写入数据库,进而加入适当的索引中。也就是说,您必须对现有的每个此类实体执行以下操作:
- 查找(获取)实体。
- 将实体写回(放入)您的数据库中。
同样,将属性从“已编入索引”状态更改为“已排除”状态只会影响在此之后写入数据库的实体。任何具有该属性的现有实体的索引条目将继续存在,直到这些实体被更新或删除。为避免不需要的结果,必须完全清除按照该属性(现已被排除)进行过滤或排序的所有查询的代码。
索引限制
Datastore 模式 Firestore 会限制可与单个实体关联的索引条目的数量和总体大小。这些限制很松,不影响大部分应用。但在某些情况下,您可能会受到这些限制。
如上文所述,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|)
个,即将 9 个条目变成了 6 个:
('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 模式的单个索引,也可以将 gcloud datastore
与 index.yaml 文件搭配使用,以管理数据库下的所有索引。
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
(对于非祖先索引)。