读修复
读修复是在读取请求期间修复数据副本的过程。如果在给定读取一致性级别下参与读取请求的所有副本都一致,则数据将返回给客户端,不需要进行读修复。但是,如果在给定读取一致性级别下参与读取请求的副本不一致,则会执行读修复以使参与读取请求的副本一致。最新的数据将返回给客户端。读修复在前景中运行,并且是阻塞的,因为只有在读修复完成并构建了最新数据后才会将响应返回给客户端。
单调仲裁读取的预期
Cassandra 使用阻塞读修复来确保“单调仲裁读取”的预期,即在两次连续的仲裁读取中,保证第二次读取不会获得比第一次读取更旧的内容,即使失败的仲裁写入仅将最新值的写入写入少数副本。 “仲裁”是指副本中大多数节点。
单调读取的表级配置
Cassandra 4.0 添加了对单调读取的表级配置的支持 (CASSANDRA-14635)。read_repair
表选项已添加到表模式中,选项包括 blocking
(默认)和 none
。
read_repair
选项配置读修复行为,以允许针对各种性能和一致性行为进行调整。读修复行为会影响两个一致性属性。
-
单调仲裁读取:由
BLOCKING
提供。单调仲裁读取可防止读取在某些情况下出现时间倒流。当不提供单调仲裁读取并且写入未能到达仲裁副本时,它可能在一次读取中可见,然后在后续读取中消失。 -
写入原子性:由
NONE
提供。写入原子性可防止读取返回部分应用的写入。Cassandra 尝试提供分区级写入原子性,但由于只有由SELECT
语句覆盖的数据才会被读修复修复,因此当以比写入更细粒度的级别读取数据时,读修复可能会破坏写入原子性。例如,如果您将多个行写入批处理中的集群分区,但随后通过在SELECT
语句中指定集群列来选择单个行,则读修复可能会破坏写入原子性。
可用的读修复设置如下
读修复示例
为了用示例说明读修复,假设客户端向 5 节点集群发送读取请求,读取一致性级别为 TWO
,如图 1 所示。读取一致性级别决定了多少个副本节点必须返回响应才能使读取请求被视为成功。
图 1. 客户端向 5 节点集群发送读取请求
三个节点托管请求数据的副本,如图 2 所示。在读取一致性级别为 TWO
的情况下,两个副本节点必须返回响应才能使读取请求被视为成功。如果客户端发送请求的节点托管请求数据的副本,则只需要将读取请求发送到另一个副本节点。但是,如果接收节点没有托管请求数据的副本,则该节点将成为协调器节点,并将读取请求转发到托管副本的节点。直接读取请求将转发到最快的节点(由动态探测器确定),如图 2 所示。直接读取请求是完整读取,并返回请求的数据。
图 2. 发送到最快的副本节点的直接读取请求
接下来,协调器节点向必要的其他节点发送必需数量的额外请求以满足一致性级别,即 TWO
。协调器节点需要发送一个额外的读取请求,总共两个。除了第一个直接读取请求之外的所有读取请求都是摘要读取请求。摘要读取请求不是完整读取,只返回数据的哈希值。只返回哈希值是为了减少网络数据流量。在讨论的示例中,协调器节点向托管副本的节点发送一个摘要读取请求,如图 3 所示。
图 3. 协调器发送摘要读取请求
协调器节点已从一个节点接收了数据的完整副本,并从另一个节点接收了数据的哈希值。为了比较返回的数据,将计算数据的完整副本的哈希值。两个哈希值将进行比较。如果哈希值相同,则不需要进行读修复,并且数据的完整副本将返回给客户端。协调器节点只执行了总共两个副本读取请求,因为示例中的读取一致性级别为 TWO
。如果一致性级别更高,例如 THREE
,则三个副本节点需要响应读取请求,并且只有当所有摘要或哈希值与数据的完整副本的哈希值匹配时,读取请求才会被视为成功,并且数据将返回给客户端。
但是,如果摘要读取请求/s 的哈希值/s 与第一个副本节点的数据的哈希值不同,则意味着副本中存在不一致。为了修复不一致,将执行读修复。
例如,假设摘要请求返回的哈希值与直接完整读取请求的数据的哈希值不同。我们需要使副本一致,为此,协调器节点向之前发送过摘要读取请求的副本节点发送直接(完整)读取请求,如图 4 所示。
图 4. 协调器向之前发送过摘要读取请求的副本节点发送直接读取请求
在从第二个副本节点接收数据后,协调器拥有来自两个副本节点的数据。它只需要两个副本,因为示例中的读取一致性级别为 TWO
。比较来自两个副本的数据,并根据时间戳选择最新的副本。如果一个副本只包含部分列的数据,则可能需要合并数据以构建数据的最新副本。在示例中,如果发现第一个直接读取请求的数据已过期,而第二个完整读取请求的数据是最新的,则需要对副本 2 执行读修复。如果通过合并两个副本构建了新的最新数据,则需要对两个参与的副本执行读修复。例如,如图 5 所示,对副本 2 执行读修复。
图 5. 协调器执行读修复
如图 6 所示,最新数据将返回给客户端。在三个副本中,副本 1 甚至没有被读取,因此没有被修复。副本 2 已被修复。副本 3 是最新的,并返回给客户端。
图 6. 返回给客户端的最新数据
读取一致性级别和读取修复
读取一致性在确定是否需要执行读取修复方面最为重要。如表 1 所述,并非所有一致性级别都需要读取修复。
表 1. 基于读取一致性级别的读取修复
读取一致性级别 | 描述 |
---|---|
ONE |
不执行读取修复,因为来自第一个直接读取请求的数据满足一致性级别 ONE。没有涉及摘要读取请求来查找数据中的不匹配。 |
TWO |
如果直接读取请求和摘要读取请求确定数据存在不一致,则执行读取修复。 |
THREE |
如果直接读取请求和摘要读取请求确定数据存在不一致,则执行读取修复。 |
LOCAL_ONE |
不执行读取修复,因为来自最接近副本的直接读取请求的数据满足一致性级别 LOCAL_ONE。没有涉及摘要读取请求来查找数据中的不匹配。 |
LOCAL_QUORUM |
如果直接读取请求和摘要读取请求确定数据存在不一致,则执行读取修复。 |
QUORUM |
如果直接读取请求和摘要读取请求确定数据存在不一致,则执行读取修复。 |
如果执行读取修复,则仅对参与读取请求且未更新的副本进行修复。参与读取请求的副本数量将基于读取一致性级别;在本例中为两个。
Cassandra 4.0 中改进的读取修复阻塞行为
Cassandra 4.0 对读取修复阻塞行为进行了两项改进 (CASSANDRA-10726)。
-
完整数据读取请求的推测重试。Cassandra 4.0 在向副本发送读取请求(完整,而不是摘要)时使用推测重试,无论是在初始完整读取请求中还是在读取修复期间的完整数据读取请求中,只要没有收到完整数据响应。使用推测重试,如果看起来可能无法从 Cassandra 发送消息到的初始副本集中收到响应以满足一致性级别,它会推测性地向未联系的副本发送额外的读取请求。Cassandra 4.0 还将推测性地向未参与读取修复数据读取/写入周期的少数节点发送修复变异,其中包含所有未确认变异的组合内容,如果看起来其中一个可能无法响应。只要接收到的确认数量与传输的修复变异数量相同,Cassandra 就会接受来自它们的确认,以代替来自最初发送的变异的确认。
-
仅在完整数据响应上阻塞以满足一致性级别。Cassandra 4.0 仅阻塞解决摘要不匹配所需的内容,并等待足够多的完整数据响应以满足一致性级别,无论它是推测重试还是读取修复机会。例如,如果看起来 Cassandra 可能无法及时收到来自所有人的完整数据请求,它会向初始完整数据读取中未联系的额外副本发送额外的请求。如果最终及时响应的节点集合最终对数据达成一致,则来自启动读取修复的不同意副本的响应将不被考虑,也不会包含在对客户端的响应中,从而保留了单调一致性读取的预期。
读取修复的诊断事件
Cassandra 4.0 为读取修复添加了诊断事件 (CASSANDRA-14668),可用于公开以下信息
-
联系的端点
-
按端点的摘要响应
-
受影响的分区键
-
推测读取/写入
-
更新过大
后台读取修复
后台读取修复(以前使用 cassandra.yaml
中的 read_repair_chance
和 dclocal_read_repair_chance
设置配置)已从 Cassandra 4.0 中删除 (CASSANDRA-13910)。
读取修复不是其他类型的修复(如完整修复或替换持续失败的节点)的替代方案。即使执行了读取修复,如果一致性级别不是要求所有副本响应的级别,则返回的数据可能不是最新的数据。