replace all 二级索引&辅助索引 with 次级索引(secondary index)

This commit is contained in:
Gang Yin 2021-10-21 15:28:23 +08:00
parent 221c8708e3
commit 1d4b5106e2
7 changed files with 27 additions and 27 deletions

View File

@ -87,7 +87,7 @@
* 数据库通常保留数据直至显式删除,而大多数消息代理在消息成功递送给消费者时会自动删除消息。这样的消息代理不适合长期的数据存储。
* 由于它们很快就能删除消息,大多数消息代理都认为它们的工作集相当小—— 即队列很短。如果代理需要缓冲很多消息比如因为消费者速度较慢如果内存装不下消息可能会溢出到磁盘每个消息需要更长的处理时间整体吞吐量可能会恶化【6】。
* 数据库通常支持级索引和各种搜索数据的方式,而消息代理通常支持按照某种模式匹配主题,订阅其子集。虽然机制并不一样,但对于客户端选择想要了解的数据的一部分,都是基本的方式。
* 数据库通常支持级索引和各种搜索数据的方式,而消息代理通常支持按照某种模式匹配主题,订阅其子集。虽然机制并不一样,但对于客户端选择想要了解的数据的一部分,都是基本的方式。
* 查询数据库时,结果通常基于某个时间点的数据快照;如果另一个客户端随后向数据库写入一些改变了查询结果的内容,则第一个客户端不会发现其先前结果现已过期(除非它重复查询或轮询变更)。相比之下,消息代理不支持任意查询,但是当数据发生变化时(即新消息可用时),它们会通知客户端。
这是关于消息代理的传统观点它被封装在诸如JMS 【14】和AMQP 【15】的标准中并且被诸如RabbitMQ、ActiveMQ、HornetQ、Qpid、TIBCO企业消息服务、IBM MQ、Azure Service Bus和Google Cloud Pub/Sub所实现 【16】。

View File

@ -105,9 +105,9 @@
具有良好定义的输入和输出的确定性函数的原理不仅有利于容错(请参阅“[幂等性](ch11.md#幂等性)”也简化了有关组织中数据流的推理【7】。无论衍生数据是搜索索引、统计模型还是缓存采用这种观点思考都是很有帮助的将其视为从一个东西衍生出另一个的数据管道通过函数式应用代码推送一个系统的状态变更并将其效果应用至衍生系统中。
原则上,衍生数据系统可以同步地维护,就像关系数据库在与索引表写入操作相同的事务中同步更新辅助索引一样。然而,异步是使基于事件日志的系统稳健的原因:它允许系统的一部分故障被抑制在本地。而如果任何一个参与者失败,分布式事务将中止,因此它们倾向于通过将故障传播到系统的其余部分来放大故障(请参阅“[分布式事务的限制](ch9.md#分布式事务的限制)”)。
原则上,衍生数据系统可以同步地维护,就像关系数据库在与索引表写入操作相同的事务中同步更新次级索引一样。然而,异步是使基于事件日志的系统稳健的原因:它允许系统的一部分故障被抑制在本地。而如果任何一个参与者失败,分布式事务将中止,因此它们倾向于通过将故障传播到系统的其余部分来放大故障(请参阅“[分布式事务的限制](ch9.md#分布式事务的限制)”)。
我们在“[分区与次级索引](ch6.md#分区与次级索引)”中看到,二级索引经常跨越分区边界。具有二级索引的分区系统需要将写入发送到多个分区如果索引按关键词分区的话或将读取发送到所有分区如果索引是按文档分区的话。如果索引是异步维护的这种跨分区通信也是最可靠和最可伸缩的【8】另请参阅“[多分区数据处理](多分区数据处理)”)。
我们在“[分区与次级索引](ch6.md#分区与次级索引)”中看到,次级索引经常跨越分区边界。具有次级索引的分区系统需要将写入发送到多个分区如果索引按关键词分区的话或将读取发送到所有分区如果索引是按文档分区的话。如果索引是异步维护的这种跨分区通信也是最可靠和最可伸缩的【8】另请参阅“[多分区数据处理](多分区数据处理)”)。
#### 应用演化后重新处理数据
@ -261,7 +261,7 @@
用于次级索引的衍生函数是如此常用的需求,以致于它作为核心功能被内建至许多数据库中,你可以简单地通过`CREATE INDEX`来调用它。对于全文索引常见语言的基本语言特征可能内置到数据库中但更复杂的特征通常需要领域特定的调整。在机器学习中特征工程是众所周知的特定于应用的特征通常需要包含很多关于用户交互与应用部署的详细知识【35】。
当创建衍生数据集的函数不是像创建级索引那样的标准搬砖函数时,需要自定义代码来处理特定于应用的东西。而这个自定义代码是让许多数据库挣扎的地方,虽然关系数据库通常支持触发器、存储过程和用户定义的函数,可以用它们来在数据库中执行应用代码,但它们有点像数据库设计里的事后反思。(请参阅“[传递事件流](ch11.md#传递事件流)”)。
当创建衍生数据集的函数不是像创建级索引那样的标准搬砖函数时,需要自定义代码来处理特定于应用的东西。而这个自定义代码是让许多数据库挣扎的地方,虽然关系数据库通常支持触发器、存储过程和用户定义的函数,可以用它们来在数据库中执行应用代码,但它们有点像数据库设计里的事后反思。(请参阅“[传递事件流](ch11.md#传递事件流)”)。
#### 应用代码和状态的分离

10
ch3.md
View File

@ -299,17 +299,17 @@ B树在数据库体系结构中是非常根深蒂固的为许多工作负载
到目前为止,我们只讨论了键值索引,它们就像关系模型中的**主键primary key** 索引。主键唯一标识关系表中的一行或文档数据库中的一个文档或图形数据库中的一个顶点。数据库中的其他记录可以通过其主键或ID引用该行/文档/顶点,并且索引用于解析这样的引用。
级索引也很常见。在关系数据库中,您可以使用 `CREATE INDEX` 命令在同一个表上创建多个级索引,而且这些索引通常对于有效地执行联接而言至关重要。例如,在[第二章](ch2.md)中的[图2-1](img/fig2-1.png)中,很可能在 `user_id` 列上有一个级索引,以便您可以在每个表中找到属于同一用户的所有行。
级索引也很常见。在关系数据库中,您可以使用 `CREATE INDEX` 命令在同一个表上创建多个级索引,而且这些索引通常对于有效地执行联接而言至关重要。例如,在[第二章](ch2.md)中的[图2-1](img/fig2-1.png)中,很可能在 `user_id` 列上有一个级索引,以便您可以在每个表中找到属于同一用户的所有行。
一个级索引可以很容易地从一个键值索引构建。主要的不同是键不是唯一的。即可能有许多行文档顶点具有相同的键。这可以通过两种方式来解决或者通过使索引中的每个值成为匹配行标识符的列表如全文索引中的发布列表或者通过向每个索引添加行标识符来使每个关键字唯一。无论哪种方式B树和日志结构索引都可以用作辅助索引。
一个级索引可以很容易地从一个键值索引构建。主要的不同是键不是唯一的。即可能有许多行文档顶点具有相同的键。这可以通过两种方式来解决或者通过使索引中的每个值成为匹配行标识符的列表如全文索引中的发布列表或者通过向每个索引添加行标识符来使每个关键字唯一。无论哪种方式B树和日志结构索引都可以用作次级索引。
#### 将值存储在索引中
索引中的键是查询搜索的内容,而其值可以是以下两种情况之一:它可以是所讨论的实际行(文档,顶点),也可以是对存储在别处的行的引用。在后一种情况下,行被存储的地方被称为**堆文件heap file**,并且存储的数据没有特定的顺序(它可以是仅追加的,或者可以跟踪被删除的行以便用新数据覆盖它们后来)。堆文件方法很常见,因为它避免了在存在多个级索引时复制数据:每个索引只引用堆文件中的一个位置,实际的数据保存在一个地方。
索引中的键是查询搜索的内容,而其值可以是以下两种情况之一:它可以是所讨论的实际行(文档,顶点),也可以是对存储在别处的行的引用。在后一种情况下,行被存储的地方被称为**堆文件heap file**,并且存储的数据没有特定的顺序(它可以是仅追加的,或者可以跟踪被删除的行以便用新数据覆盖它们后来)。堆文件方法很常见,因为它避免了在存在多个级索引时复制数据:每个索引只引用堆文件中的一个位置,实际的数据保存在一个地方。
在不更改键的情况下更新值时堆文件方法可以非常高效只要新值的字节数不大于旧值就可以覆盖该记录。如果新值更大情况会更复杂因为它可能需要移到堆中有足够空间的新位置。在这种情况下要么所有的索引都需要更新以指向记录的新堆位置或者在旧堆位置留下一个转发指针【5】。
在某些情况下从索引到堆文件的额外跳跃对读取来说性能损失太大因此可能希望将索引行直接存储在索引中。这被称为聚集索引。例如在MySQL的InnoDB存储引擎中表的主键总是一个聚集索引级索引则引用主键而不是堆文件中的位置【31】。在SQL Server中可以为每个表指定一个聚集索引【32】。
在某些情况下从索引到堆文件的额外跳跃对读取来说性能损失太大因此可能希望将索引行直接存储在索引中。这被称为聚集索引。例如在MySQL的InnoDB存储引擎中表的主键总是一个聚集索引级索引则引用主键而不是堆文件中的位置【31】。在SQL Server中可以为每个表指定一个聚集索引【32】。
**聚集索引clustered index** (在索引中存储所有行数据)和 **非聚集索引nonclustered index** (仅在索引中存储对数据的引用)之间的折衷被称为 **包含列的索引index with included columns** 或**覆盖索引covering index**其存储表的一部分在索引内【33】。这允许通过单独使用索引来回答一些查询这种情况叫做索引 **覆盖cover** 了查询【32】。
@ -548,7 +548,7 @@ WHERE product_sk = 31 AND store_sk = 3
这个想法的巧妙扩展在C-Store中引入并在商业数据仓库Vertica【61,62】中被采用。不同的查询受益于不同的排序顺序为什么不以几种不同的方式来存储相同的数据呢无论如何数据需要复制到多台机器这样如果一台机器发生故障您不会丢失数据。您可能还需要存储以不同方式排序的冗余数据以便在处理查询时可以使用最适合查询模式的版本。
在一个面向列的存储中有多个排序顺序有点类似于在一个面向行的存储中有多个级索引。但最大的区别在于面向行的存储将每一行保存在一个地方(在堆文件或聚簇索引中),级索引只包含指向匹配行的指针。在列存储中,通常在其他地方没有任何指向数据的指针,只有包含值的列。
在一个面向列的存储中有多个排序顺序有点类似于在一个面向行的存储中有多个级索引。但最大的区别在于面向行的存储将每一行保存在一个地方(在堆文件或聚簇索引中),级索引只包含指向匹配行的指针。在列存储中,通常在其他地方没有任何指向数据的指针,只有包含值的列。
### 写入列存储

2
ch5.md
View File

@ -573,7 +573,7 @@
然而,在无领导者复制的系统中,没有固定的写入顺序,这使得监控变得更加困难。而且,如果数据库只使用读修复(没有反熵过程),那么对于一个值可能会有多大的限制是没有限制的 - 如果一个值很少被读取,那么由一个陈旧副本返回的值可能是古老的。
已经有一些关于衡量无主复制数据库中的复制陈旧度的研究并根据参数nw和r来预测陈旧读取的预期百分比【48】。不幸的是这还不是很常见的做法但是将陈旧测量值包含在数据库的度量标准集中是一件好事。最终一致性是一种非常模糊的保证,但是从可操作性角度来说,能够量化“最终”是很重要的。
已经有一些关于衡量无主复制数据库中的复制陈旧度的研究并根据参数nw和r来预测陈旧读取的预期百分比【48】。不幸的是这还不是很常见的做法但是将陈旧测量值包含在数据库的度量标准集中是一件好事。虽然最终一致性是一种有意模糊的保证,但是从可操作性角度来说,能够量化“最终”是很重要的。
### 宽松的法定人数与提示移交

28
ch6.md
View File

@ -121,29 +121,29 @@
次级索引是关系型数据库的基础并且在文档数据库中也很普遍。许多键值存储如HBase和Volde-mort为了减少实现的复杂度而放弃了次级索引但是一些如Riak已经开始添加它们因为它们对于数据模型实在是太有用了。并且次级索引也是Solr和Elasticsearch等搜索服务器的基石。
次级索引的问题是它们不能整齐地映射到分区。有两种用级索引对数据库进行分区的方法:**基于文档的分区document-based** 和**基于关键词term-based的分区**。
次级索引的问题是它们不能整齐地映射到分区。有两种用级索引对数据库进行分区的方法:**基于文档的分区document-based** 和**基于关键词term-based的分区**。
### 基于文档的级索引进行分区
### 基于文档的级索引进行分区
假设你正在经营一个销售二手车的网站(如[图6-4](img/fig6-4.png)所示)。 每个列表都有一个唯一的ID——称之为文档ID——并且用文档ID对数据库进行分区例如分区0中的ID 0到499分区1中的ID 500到999等
你想让用户搜索汽车,允许他们通过颜色和厂商过滤,所以需要一个在颜色和厂商上的次级索引(文档数据库中这些是**字段field**,关系数据库中这些是**列column** )。 如果您声明了索引,则数据库可以自动执行索引[^ii]。例如,无论何时将红色汽车添加到数据库,数据库分区都会自动将其添加到索引条目`color:red`的文档ID列表中。
[^ii]: 如果数据库仅支持键值模型则你可能会尝试在应用程序代码中创建从值到文档ID的映射来实现辅助索引。 如果沿着这条路线走下去,请万分小心,确保您的索引与底层数据保持一致。 竞争条件和间歇性写入失败(其中一些更改已保存,但其他更改未保存)很容易导致数据不同步 - 请参阅“[多对象事务的需求](ch7.md#多对象事务的需求)”。
[^ii]: 如果数据库仅支持键值模型则你可能会尝试在应用程序代码中创建从值到文档ID的映射来实现次级索引。 如果沿着这条路线走下去,请万分小心,确保您的索引与底层数据保持一致。 竞争条件和间歇性写入失败(其中一些更改已保存,但其他更改未保存)很容易导致数据不同步 - 请参阅“[多对象事务的需求](ch7.md#多对象事务的需求)”。
![](img/fig6-4.png)
**图6-4 基于文档的级索引进行分区**
**图6-4 基于文档的级索引进行分区**
在这种索引方法中,每个分区是完全独立的:每个分区维护自己的级索引仅覆盖该分区中的文档。它不关心存储在其他分区的数据。无论何时您需要写入数据库添加删除或更新文档只需处理包含您正在编写的文档ID的分区即可。出于这个原因**文档分区索引**也被称为**本地索引local index**(而不是将在下一节中描述的**全局索引global index**)。
在这种索引方法中,每个分区是完全独立的:每个分区维护自己的级索引仅覆盖该分区中的文档。它不关心存储在其他分区的数据。无论何时您需要写入数据库添加删除或更新文档只需处理包含您正在编写的文档ID的分区即可。出于这个原因**文档分区索引**也被称为**本地索引local index**(而不是将在下一节中描述的**全局索引global index**)。
但是从文档分区索引中读取需要注意除非您对文档ID做了特别的处理否则没有理由将所有具有特定颜色或特定品牌的汽车放在同一个分区中。在[图6-4](img/fig6-4.png)中红色汽车出现在分区0和分区1中。因此如果要搜索红色汽车则需要将查询发送到所有分区并合并所有返回的结果。
这种查询分区数据库的方法有时被称为**分散/聚集scatter/gather**,并且可能会使级索引上的读取查询相当昂贵。即使并行查询分区,分散/聚集也容易导致尾部延迟放大(请参阅“[实践中的百分位点](ch1.md#实践中的百分位点)”。然而它被广泛使用MongoDBRiak 【15】Cassandra 【16】Elasticsearch 【17】SolrCloud 【18】和VoltDB 【19】都使用文档分区二级索引。大多数数据库供应商建议您构建一个能从单个分区提供二级索引查询的分区方案,但这并不总是可行,尤其是当在单个查询中使用多个级索引时(例如同时需要按颜色和制造商查询)。
这种查询分区数据库的方法有时被称为**分散/聚集scatter/gather**,并且可能会使级索引上的读取查询相当昂贵。即使并行查询分区,分散/聚集也容易导致尾部延迟放大(请参阅“[实践中的百分位点](ch1.md#实践中的百分位点)”。然而它被广泛使用MongoDBRiak 【15】Cassandra 【16】Elasticsearch 【17】SolrCloud 【18】和VoltDB 【19】都使用文档分区次级索引。大多数数据库供应商建议您构建一个能从单个分区提供次级索引查询的分区方案,但这并不总是可行,尤其是当在单个查询中使用多个级索引时(例如同时需要按颜色和制造商查询)。
### 基于关键词(Term)的级索引进行分区
### 基于关键词(Term)的级索引进行分区
我们可以构建一个覆盖所有分区数据的**全局索引**,而不是给每个分区创建自己的次级索引(本地索引)。但是,我们不能只把这个索引存储在一个节点上,因为它可能会成为瓶颈,违背了分区的目的。全局索引也必须进行分区,但可以采用与主键不同的分区方式。
@ -151,7 +151,7 @@
![](img/fig6-5.png)
**图6-5 基于关键词对级索引进行分区**
**图6-5 基于关键词对级索引进行分区**
我们将这种索引称为**关键词分区term-partitioned**,因为我们寻找的关键词决定了索引的分区方式。例如,一个关键词可能是:`color:red`。**关键词(Term)** 这个名称来源于全文搜索索引(一种特殊的次级索引),指文档中出现的所有单词。
@ -161,9 +161,9 @@
理想情况下,索引总是最新的,写入数据库的每个文档都会立即反映在索引中。但是,在关键词分区索引中,这需要跨分区的分布式事务,并不是所有数据库都支持(请参阅[第七章](ch7.md)和[第九章](ch9.md))。
在实践中,对全局级索引的更新通常是**异步**的也就是说如果在写入之后不久读取索引刚才所做的更改可能尚未反映在索引中。例如Amazon DynamoDB声称在正常情况下其全局次级索引会在不到一秒的时间内更新但在基础架构出现故障的情况下可能会有延迟【20】。
在实践中,对全局级索引的更新通常是**异步**的也就是说如果在写入之后不久读取索引刚才所做的更改可能尚未反映在索引中。例如Amazon DynamoDB声称在正常情况下其全局次级索引会在不到一秒的时间内更新但在基础架构出现故障的情况下可能会有延迟【20】。
全局关键词分区索引的其他用途包括Riak的搜索功能【21】和Oracle数据仓库它允许您在本地和全局索引之间进行选择【22】。我们将在[第十二章](ch12.md)中继续关键词分区级索引实现的话题。
全局关键词分区索引的其他用途包括Riak的搜索功能【21】和Oracle数据仓库它允许您在本地和全局索引之间进行选择【22】。我们将在[第十二章](ch12.md)中继续关键词分区级索引实现的话题。
## 分区再平衡
@ -286,7 +286,7 @@
### 执行并行查询
到目前为止,我们只关注读取或写入单个键的非常简单的查询(加上基于文档分区的级索引场景下的分散/聚集查询。这也是大多数NoSQL分布式数据存储所支持的访问层级。
到目前为止,我们只关注读取或写入单个键的非常简单的查询(加上基于文档分区的级索引场景下的分散/聚集查询。这也是大多数NoSQL分布式数据存储所支持的访问层级。
然而,通常用于分析的**大规模并行处理MPP, Massively parallel processing** 关系型数据库产品在其支持的查询类型方面要复杂得多。一个典型的数据仓库查询包含多个连接,过滤,分组和聚合操作。 MPP查询优化器将这个复杂的查询分解成许多执行阶段和分区其中许多可以在数据库集群的不同节点上并行执行。涉及扫描大规模数据集的查询特别受益于这种并行执行。
@ -314,10 +314,10 @@
两种方法搭配使用也是可行的,例如使用复合主键:使用键的一部分来标识分区,而使用另一部分作为排序顺序。
我们还讨论了分区和级索引之间的相互作用。次级索引也需要分区,有两种方法:
我们还讨论了分区和级索引之间的相互作用。次级索引也需要分区,有两种方法:
* 基于文档分区(本地索引),其中级索引存储在与主键和值相同的分区中。这意味着只有一个分区需要在写入时更新,但是读取级索引需要在所有分区之间进行分散/收集。
* 基于关键词分区(全局索引),其中二级索引存在不同的分区中。辅助索引中的条目可以包括来自主键的所有分区的记录。当文档写入时,需要更新多个分区中的级索引;但是可以从单个分区中进行读取。
* 基于文档分区(本地索引),其中级索引存储在与主键和值相同的分区中。这意味着只有一个分区需要在写入时更新,但是读取级索引需要在所有分区之间进行分散/收集。
* 基于关键词分区(全局索引),其中次级索引存在不同的分区中。次级索引中的条目可以包括来自主键的所有分区的记录。当文档写入时,需要更新多个分区中的级索引;但是可以从单个分区中进行读取。
最后,我们讨论了将查询路由到适当的分区的技术,从简单的分区负载平衡到复杂的并行查询执行引擎。

4
ch7.md
View File

@ -191,7 +191,7 @@ SELECT COUNT*FROM emails WHERE recipient_id = 2 AND unread_flag = true
* 在关系数据模型中,一个表中的行通常具有对另一个表中的行的外键引用。(类似的是,在一个图数据模型中,一个顶点有着到其他顶点的边)。多对象事务使你确保这些引用始终有效:当插入几个相互引用的记录时,外键必须是正确的和最新的,不然数据就没有意义。
* 在文档数据模型中,需要一起更新的字段通常在同一个文档中,这被视为单个对象——更新单个文档时不需要多对象事务。但是,缺乏连接功能的文档数据库会鼓励非规范化(请参阅“[关系型数据库与文档数据库在今日的对比](ch2.md#关系型数据库与文档数据库在今日的对比)”)。当需要更新非规范化的信息时,如 [图7-2](img/fig7-2.png) 所示,需要一次更新多个文档。事务在这种情况下非常有用,可以防止非规范化的数据不同步。
* 在具有级索引的数据库中(除了纯粹的键值存储以外几乎都有),每次更改值时都需要更新索引。从事务角度来看,这些索引是不同的数据库对象:例如,如果没有事务隔离性,记录可能出现在一个索引中,但没有出现在另一个索引中,因为第二个索引的更新还没有发生。
* 在具有级索引的数据库中(除了纯粹的键值存储以外几乎都有),每次更改值时都需要更新索引。从事务角度来看,这些索引是不同的数据库对象:例如,如果没有事务隔离性,记录可能出现在一个索引中,但没有出现在另一个索引中,因为第二个索引的更新还没有发生。
这些应用仍然可以在没有事务的情况下实现。然而,**没有原子性,错误处理就要复杂得多,缺乏隔离性,就会导致并发问题**。我们将在“[弱隔离级别](#弱隔离级别)”中讨论这些问题,并在[第十二章](ch12.md)中探讨其他方法。
@ -651,7 +651,7 @@ VoltDB还使用存储过程进行复制但不是将事务的写入结果从
由于跨分区事务具有额外的协调开销,所以它们比单分区事务慢得多。 VoltDB报告的吞吐量大约是每秒1000个跨分区写入比单分区吞吐量低几个数量级并且不能通过增加更多的机器来增加【49】。
事务是否可以是划分至单个分区很大程度上取决于应用数据的结构。简单的键值数据通常可以非常容易地进行分区,但是具有多个级索引的数据可能需要大量的跨分区协调(请参阅“[分区与次级索引](ch6.md#分区与次级索引)”)。
事务是否可以是划分至单个分区很大程度上取决于应用数据的结构。简单的键值数据通常可以非常容易地进行分区,但是具有多个级索引的数据可能需要大量的跨分区协调(请参阅“[分区与次级索引](ch6.md#分区与次级索引)”)。
#### 串行执行小结

2
ch9.md
View File

@ -586,7 +586,7 @@
因此,在单个节点上,事务的提交主要取决于数据持久化落盘的**顺序**首先是数据然后是提交记录【72】。事务提交或终止的关键决定时刻是磁盘完成写入提交记录的时刻在此之前仍有可能中止由于崩溃但在此之后事务已经提交即使数据库崩溃。因此是单一的设备连接到单个磁盘的控制器且挂载在单台机器上使得提交具有原子性。
但是,如果一个事务中涉及多个节点呢?例如,你也许在分区数据库中会有一个多对象事务,或者是一个按关键词分区的级索引(其中索引条目可能位于与主数据不同的节点上;请参阅“[分区与次级索引](ch6.md#分区与次级索引)”。大多数“NoSQL”分布式数据存储不支持这种分布式事务但是很多关系型数据库集群支持请参阅“[实践中的分布式事务](#实践中的分布式事务)”)。
但是,如果一个事务中涉及多个节点呢?例如,你也许在分区数据库中会有一个多对象事务,或者是一个按关键词分区的级索引(其中索引条目可能位于与主数据不同的节点上;请参阅“[分区与次级索引](ch6.md#分区与次级索引)”。大多数“NoSQL”分布式数据存储不支持这种分布式事务但是很多关系型数据库集群支持请参阅“[实践中的分布式事务](#实践中的分布式事务)”)。
在这些情况下,仅向所有节点发送提交请求并独立提交每个节点的事务是不够的。这样很容易发生违反原子性的情况:提交在某些节点上成功,而在其他节点上失败: