一致性
Weaviate 中的复制因子决定了分片(也称为副本)在 Weaviate 集群中存储的副本数量。

当复制因子 > 1 时,一致性模型会平衡系统的可靠性、可扩展性和/或性能要求。
Weaviate 使用多种一致性模型。一种用于其集群元数据,另一种用于其数据对象。
Weaviate 中的一致性模型
Weaviate 使用 Raft 共识算法用于 集群元数据复制。 在这种情况下,集群元数据包括集合定义和租户活动状态。 这允许即使某些节点发生故障,集群元数据更新也能发生。
数据对象使用无领导者设计进行复制,并使用可调整的一致性级别。 因此,可以根据所需的权衡来调整数据操作,使其更一致或更可用。
这些设计反映了 CAP 定理 中描述的一致性和可用性之间的权衡。
可以通过应用以下条件来确定一致性的强度
- 如果 r + w > n,则系统是强一致的。
- r 是读取操作的一致性级别
- w 是写入操作的一致性级别
- n 是复制因子(副本数量)
- 如果 r + w <= n,则最终一致性是此场景下可以达到的最佳状态。
集群元数据
Weaviate 中的集群元数据使用 Raft 算法。
从 v1.25 开始,Weaviate 使用 Raft 共识算法进行集群元数据复制。 Raft 是一种具有选定的领导节点并通过基于日志的方法在集群中协调复制的共识算法。
因此,每个更改集群元数据的请求都将发送到领导节点。 领导节点会将更改应用于其日志,然后将更改传播到跟随节点。 一旦 quorum 数量的节点确认了集群元数据更改,领导节点将提交更改并将其确认给客户端。
此架构确保即使在(少数)节点发生故障的情况下,集群元数据更改在整个集群中保持一致。
v1.25 之前的集群元数据共识算法
在采用 Raft 之前,集群元数据更新是通过 分布式事务 算法完成的。 这是一组在分布式网络中不同节点的数据库上执行的操作。 Weaviate 使用 两阶段提交 (2PC) 协议,该协议在短时间内(毫秒级)复制集群元数据更新。
干净的执行(无故障)有两个阶段
- 提交请求阶段(或投票阶段),其中协调节点询问每个节点是否能够接收和处理更新。
- 提交阶段,其中协调节点将更改提交给节点。
查询中集合定义请求
v1.27.10、v1.28.4 中添加某些查询需要集合定义。 在引入此功能之前,每次此类查询都会导致本地(请求)节点从领导节点获取集合定义。 这意味着定义是强一致的,但可能会导致额外的流量和负载。
如果可用,可以设置 环境变量 COLLECTION_RETRIEVAL_STRATEGY 为 LeaderOnly、LocalOnly 或 LeaderOnMismatch。
LeaderOnly(默认):始终从领导节点请求定义。 这是最一致的行为,但可能会导致更高的集群内流量。LocalOnly:始终使用本地定义;导致最终一致性行为,同时减少集群内流量。LeaderOnMismatch:检查本地定义是否已过时,并在必要时请求定义。 平衡一致性和集群内流量。
默认行为是 LeaderOnly 以实现强一致性。 但是,可以使用 LocalOnly 和 LeaderOnMismatch 来根据所需的一致性级别减少集群内流量。
数据对象
Weaviate 使用两阶段提交来处理对象,并根据一致性级别进行调整。 例如,对于 QUORUM 写入(见下文),如果有 5 个节点,将发送 3 个请求,每个请求在底层使用两阶段提交。
因此,Weaviate 中的数据对象是最终一致的。 最终一致性提供 BASE 语义
- 基本可用:读取和写入操作尽可能可用
- 软状态:由于更新可能尚未收敛,因此没有一致性保证
- 最终一致性:如果系统运行足够长的时间,在写入之后,所有节点都将保持一致。
Weaviate 使用最终一致性来提高可用性。 读取和写入一致性是可调整的,因此您可以根据应用程序的需求在可用性和一致性之间进行权衡。
下图是一个示例,说明了使用复制因子为 3 和 8 个节点的 Weaviate 进行写入或读取的方式。 蓝色节点充当协调节点。 一致性级别设置为 QUORUM,因此协调节点仅在发送结果给客户端之前等待 3 个响应中的 2 个。

可调整的写入一致性
添加或更改数据对象是 写入 操作。
从 Weaviate v1.18 开始,写入操作是可调整的,设置为 ONE、QUORUM(默认)或 ALL。 在 v1.17 中,写入操作始终设置为 ALL(最高一致性)。
在 v1.18 中引入可配置写入一致性的主要原因是,那时也引入了自动修复。 写入始终会写入 n(复制因子)个节点,无论选择的一致性级别如何。 但是,协调节点仅在从 ONE、QUORUM 或 ALL 节点收到确认后才会返回。 为了保证在读取请求上没有修复的情况下应用所有内容,目前写入一致性设置为 ALL。 v1.18+ 中的可能设置是
- ONE - 写入必须从至少一个副本节点收到确认。 这是最快(最可用),但一致性最低的选项。
- QUORUM - 写入必须从至少
QUORUM副本节点收到确认。QUORUM的计算方式为 n / 2 + 1,其中 n 是副本数(复制因子)。 例如,使用复制因子为 6,quorum 为 4,这意味着集群可以容忍 2 个副本发生故障。 - ALL - 写入必须从所有副本节点收到确认。 这是最一致的,但“最慢”(可用性最低)的选项。
下图:具有写入一致性为 ONE 的复制的 Weaviate 设置。 总共有 8 个节点,其中 3 个副本。

下图:具有写入一致性为 QUORUM(n/2+1)的复制的 Weaviate 设置。 总共有 8 个节点,其中 3 个副本。

下图:具有写入一致性为 ALL 的复制的 Weaviate 设置。 总共有 8 个节点,其中 3 个副本。

可调整的读取一致性
读取操作是 Weaviate 中数据对象的 GET 请求。 与写入一样,读取一致性是可调整的,设置为 ONE、QUORUM(默认)或 ALL。
在 v1.18 之前,可调的一致性读取仅适用于 通过 ID 获取对象的请求,而所有其他读取请求的一致性均为 ALL。
以下一致性级别适用于大多数读取操作
- 从
v1.18开始,一致性级别适用于 REST 端点操作。 - 从
v1.19开始,一致性级别适用于 GraphQLGet请求。 - 所有基于 gRPC 的读取和写入操作都支持可调一致性级别。
- ONE - 读取响应必须至少由一个副本返回。这是最快(可用性最高),但一致性最低的选项。
- QUORUM - 响应必须由
QUORUM数量的副本节点返回。QUORUM的计算方式为 n / 2 + 1,其中 n 是副本数(复制因子)。例如,使用复制因子 6,则法定人数为 4,这意味着集群可以容忍 2 个副本宕机。 - ALL - 读取响应必须由所有副本返回。如果至少一个副本未能响应,读取操作将失败。这是最一致,但“最慢”(可用性最低)的选项。
示例
-
ONE
在具有复制因子 3 和读取一致性级别为 ONE 的单个数据中心,协调节点将等待一个副本节点返回响应。
-
QUORUM
在具有复制因子 3 和读取一致性级别为QUORUM的单个数据中心,协调节点将等待 n / 2 + 1 = 3 / 2 + 1 = 2 个副本节点返回响应。
-
ALL
在具有复制因子 3 和读取一致性级别为ALL的单个数据中心,协调节点将等待所有 3 个副本节点返回响应。
可调一致性策略
根据一致性和速度之间的期望权衡,以下是写入/读取操作的三个常见一致性级别配对。这些是保证最终一致性数据的最低要求
QUORUM/QUORUM=> 平衡的写入和读取延迟ONE/ALL=> 快速写入和慢速读取(针对写入优化)ALL/ONE=> 慢速写入和快速读取(针对读取优化)
可调一致性和查询
请注意,读取操作的可调一致性级别不会影响查询返回的对象列表的一致性。换句话说,查询返回的对象 UUID 列表仅取决于协调节点(以及任何其他必需的分片)的本地索引,并且与读取一致性级别无关。
这是因为每个查询都由协调节点和任何其他需要回答查询的分片执行。即使将读取一致性级别设置为 ALL,也不意味着将查询多个副本并将结果合并在一起。
应用读取一致性级别的地方是在从副本中检索标识的对象。例如,如果将读取一致性级别设置为 ALL,协调节点将等待所有副本返回标识的对象。如果将读取一致性级别设置为 ONE,协调节点可以简单地从自身返回对象。
换句话说,读取一致性级别仅影响检索到的对象的版本,但不会导致更(或更少)一致的查询结果。
默认情况下,Weaviate 在插入/更新/删除时写入所有节点。因此,大多数情况下这无关紧要,因为所有分片将具有相同的本地索引。这是一种罕见的情况,可能仅在出现问题时发生,例如节点宕机或存在网络问题。
租户状态和数据对象
在 多租户集合 中,每个租户都有一个可配置的 租户状态,该状态决定了租户数据的可用性和位置。租户状态可以设置为 active、inactive 或 offloaded。
active 租户的数据应可用于查询和更新,而 inactive 或 offloaded 租户则不可用。
但是,在设置租户状态与租户数据反映(声明式)租户状态之间可能存在延迟。
因此,即使租户状态设置为 inactive 或 offloaded,租户的数据在一段时间内仍可能可用于查询。相反,即使租户状态设置为 active,租户的数据在一段时间内也可能不可用于查询和更新。
为了提高速度,租户上的数据操作独立于任何租户活动状态操作。因此,租户状态不会通过 read-on-repair 操作更新。
修复
在 Weaviate 等分布式系统中,对象副本可能由于各种原因(例如网络问题、节点故障或时序冲突)而变得不一致。当 Weaviate 检测到副本之间的数据不一致时,它会尝试修复不同步的数据。
Weaviate 使用 异步复制、删除解析 和 read-on-repair 策略来维护副本之间的一致性。
异步复制
v1.26 中添加异步复制是 Weaviate 中的一种后台同步过程,可确保跨存储相同数据的节点最终一致性。当每个分片跨多个节点复制时,异步复制保证通过定期比较和传播数据,所有保存相同数据的节点保持同步。
它使用 Merkle 树(哈希树)算法来监视和比较集群内节点的状态。如果该算法检测到不一致,它将重新同步不一致节点上的数据。
read-on-repair 适用于一个或两个孤立的修复。异步复制在存在许多不一致的情况下有效。例如,如果离线节点错过了多个更新,异步复制在节点恢复服务时会迅速恢复一致性。
异步复制补充了 read-on-repair 机制。如果节点在同步检查之间变得不一致,read-on-repair 机制会在读取时捕获问题。
要激活异步复制,请在集合定义中的 replicationConfig 部分 将 asyncEnabled 设置为 true。访问 如何:复制 页面,了解有关可用异步复制设置的更多信息。
异步复制的内存和性能注意事项
v1.29 中添加异步复制使用哈希树来比较和同步数据库集群节点之间的数据,基于对象最新的更新时间。此过程所需的额外内存由哈希树的高度 (H) 决定。较高的哈希树使用更多的内存,但允许更快的哈希,从而减少检测和修复不一致性所需的时间。
权衡可以概括如下
- 较高
H:更高的内存使用量,更快的复制。 - 较低
H:更低的内存使用量,更慢的复制。
每个租户都由一个分片支持。因此,当租户数量很多时,异步复制的内存消耗可能很大。(例如,具有 16 哈希树高度的 1,000 个租户将需要每个节点大约 2 GB 的额外内存,而高度为 20 的则需要每个节点大约 34 GB)。
为了减少内存消耗,请降低哈希树高度。请记住,这将导致哈希速度变慢,并可能导致复制速度变慢。
使用以下公式和示例作为快速参考
内存计算
-
哈希树中的节点总数:对于高度为
H的哈希树,节点总数为Number of hash tree nodes = 2^(H+1) - 1 ≈ 2^(H+1) -
所需的总内存(每个分片/租户的每个节点):每个哈希树节点使用大约 16 字节 的内存。
Memory Required ≈ 2^(H+1) * 16 bytes
示例
-
高度为
16的哈希树哈希树中的节点总数 ≈ 2^(16+1) = 131,072所需的内存 ≈ 131072 * 16 字节 ≈ 2,097,152 字节(~2 MB)
-
高度为
20的哈希树哈希树中的节点总数 ≈ 2^(20+1) = 2,097,152所需的内存 ≈ 2,097,152 * 16 字节 ≈ 33,554,432 字节(~33 MB)
性能考虑因素:叶子数
分片(例如租户)中的对象分布在哈希树的叶子中。较大的哈希树意味着每个叶子需要哈希的数据更少,从而导致更快的比较和更快的复制。
- 哈希树中的叶子数
Number of leaves = 2^H
示例
-
高度为
16的哈希树叶子数 = 2^16 = 65,536
-
高度为
20的哈希树叶子数 = 2^20 = 1,048,576
默认哈希树高度为 16,旨在平衡内存消耗与复制性能。根据您的集群节点可用资源和性能要求调整此值。
删除解析策略
v1.28 中添加当对象存在于某些副本中,但不在其他副本中时,这可能是因为创建尚未传播到所有副本,或者因为删除尚未传播到所有副本。区分这两种情况非常重要。
删除解析与异步复制和读时修复协同工作,以确保集群中一致处理已删除的对象。对于每个集合,您可以设置以下删除解析策略之一
NoAutomatedResolutionDeleteOnConflictTimeBasedResolution
删除解析策略是可变的。 了解更多关于如何更新集合定义的信息。
NoAutomatedResolution
这是默认设置,也是 Weaviate v1.28 之前的版本中唯一的可用设置。在此模式下,Weaviate 不会将删除冲突视为特殊情况。如果对象存在于某些副本中,但不在其他副本中,Weaviate 可能会在缺少该对象的副本上恢复该对象。
DeleteOnConflict
在 deleteOnConflict 中,删除冲突始终通过在所有副本上删除对象来解决。
为此,Weaviate 在收到删除请求时,将对象更新为副本上的已删除对象,而不是删除对象的所有痕迹。
TimeBasedResolution
在 timeBasedResolution 中,删除冲突根据删除请求的时间戳与对象后续更新(例如创建或更新)的时间戳进行比较来解决。
如果删除请求的时间戳晚于任何后续更新的时间戳,则该对象将在所有副本上删除。如果删除请求的时间戳早于任何后续更新的时间戳,则将应用后续更新到所有副本。
例如
- 如果对象在时间戳 100 时被删除,然后在时间戳 90 时被重新创建,则重新创建获胜
- 如果对象在时间戳 100 时被删除,然后在时间戳 110 时被重新创建,则删除获胜
选择策略
- 如果您需要最大程度的控制并手动处理冲突,请使用
NoAutomatedResolution - 如果您希望始终确保删除操作,请使用
DeleteOnConflict - 如果您希望最新的操作优先,请使用
TimeBasedResolution
读时修复
v1.18 中添加如果您的读一致性设置为 All 或 Quorum,则读取协调器将从多个副本接收响应。如果这些响应不同,协调器可以尝试修复不一致,如下例所示。此过程称为“读时修复”或“读取修复”。
| 问题 | 操作 |
|---|---|
| 对象在某些副本上不存在。 | 将对象传播到缺失的副本。 |
| 对象已过时。 | 更新过时副本上的对象。 |
| 对象在某些副本上已被删除。 | 返回错误。删除可能已失败,或者对象可能已被部分重新创建。 |
读修复过程还取决于使用的读写一致性级别。
| 写入一致性级别 | 读一致性级别 | 操作 | | :- | :- | | ONE | ALL | Weaviate 必须验证所有节点以保证修复。 | | QUORUM | QUORUM 或 ALL | Weaviate 尝试修复同步问题。 | | ALL | - | 不应发生这种情况。写入应该已经失败。 |
修复仅在读取时发生,因此不会产生大量的后台开销。虽然节点处于不一致状态,但具有 ONE 一致性级别的读取操作可能会返回陈旧数据。
副本移动
v1.32 中添加分片代表单个租户集合中的集合的一部分,或多租户集合中的整个租户。Weaviate 允许用户手动将单个分片副本从源节点移动或复制到 Weaviate 集群中的目标节点。此功能解决了诸如缩放后的集群重新平衡、节点退役、优化数据局部性以提高性能或提高数据可用性等操作场景。
副本移动作为状态机运行,具有确保整个过程数据完整性的阶段。此功能适用于单租户集合和多租户集合。
与在集合创建时配置的静态复制因子不同,副本移动允许针对特定分片调整复制因子,因为副本在集群中移动或复制。当执行复制操作时,为该特定分片创建的新副本会增加复制因子。虽然集合可能具有默认复制因子,但集合内的单个分片可以具有更高的复制因子。但是,分片的复制因子不能低于集合级别设置的复制因子。
可以使用 REPLICATION_ENGINE_MAX_WORKERS 环境变量来调整并行处理副本移动的 worker 数量。
移动状态
每个副本移动操作都经过一个工作流程,旨在维护数据一致性和可用性。工作流程包括以下状态
-
REGISTERED:移动操作已由 Raft leader 发起并记录。请求已收到,并且操作已排队进行处理。
-
HYDRATING:正在目标节点上创建新的副本。数据段从现有副本(通常是源副本或另一个可用对等体)传输以建立新的副本。
-
FINALIZING:批量数据传输完成,并且新的副本正在赶上在传输期间发生的任何写入。这可确保副本与最新数据完全同步。您可以使用
REPLICA_MOVEMENT_MINIMUM_ASYNC_WAIT环境变量来调整等待时间,以确保任何正在进行中的写入都已完成并复制到目标节点。 -
DEHYDRATING:对于移动操作,在源节点上准备好新的副本后,原始副本正在被删除。
-
READY:操作已成功完成。新的副本已完全同步并准备好提供流量。对于移动操作,源副本已被删除。
-
CANCELLED:操作在完成前已被取消。这可能是通过手动干预或如果操作遇到无法恢复的错误发生的。
副本移动支持两种不同的操作模式
- Move:将副本从一个节点移动到另一个节点,保持相同的复制因子
- Copy:将副本从一个节点复制到另一个节点,并为该特定分片增加分片复制因子
当复制分片副本时,增加的复制因子可能会变成偶数。这可能会使实现仲裁更加困难,因为它现在需要 (n/2 + 1) 个节点,而不是 (n/2 + 0.5) 个节点。例如,从 RF=3 变为 RF=4 会将仲裁所需的节点数从 2 增加到 3(从 67% 变为 75% 的副本)。
相关页面
问题和反馈
如果您有任何问题或反馈,请在 用户论坛 中告诉我们。
