多人协同编辑算法 —— CRDT 算法
当前位置:点晴教程→知识管理交流
→『 技术文档交流 』
什么是 CRDT无冲突复制数据类型(CRDT,Conflict-free Replicated Data Types)是一类在分布式系统中用于数据复制的数据结构,旨在解决多副本并发更新时的数据一致性问题。CRDT 允许各个副本独立且并发地进行更新,而无需协调,且能够在最终自动解决可能出现的不一致性。 CRDT 的关键特性主要有以下三个方面:
CRDT 的种类CRDT 有两种方法,都可以提供强最终一致性:基于操作的 CRDT 和基于状态的 CRDT。 基于操作的 CRDT(CmRDT)基于操作的 CRDT(也称为交换性复制数据类型,CmRDT)是一类通过传输更新操作来同步副本的 CRDT。在 CmRDT 中,每个副本只发送更新操作,而不是完整的状态。例如,操作可以是"+10"或"-20",它们表示对某个值的增减。副本接收到这些操作后,会在本地应用这些更新。 操作是可交换的,这意味着操作的顺序不影响最终结果。也就是说,即使操作以不同的顺序应用,最终的结果也会是一样的。然而,这些操作不一定是幂等的,即重复应用相同操作可能会产生不同的结果。 由于操作是以独立的方式广播的,通信基础设施必须保证所有操作都被传输到所有副本,而且操作不会丢失或重复。在此过程中,操作的顺序是灵活的,可以按照任何顺序传输。 纯基于操作的 CRDT(Pure CmRDT) 是基于操作的 CRDT 的一个变种,它通过减少所需的元数据大小来优化性能。 G-CounterG-Counter 用于实现分布式环境中的计数器功能,由多个计数器组成的数据结构,每个副本都维护自己的计数器。每当副本需要增加计数时,它只会在自己的计数器上增加,而不会减少或修改其他副本的计数器。当需要获取计数时,副本会将所有计数器的值累加起来,以获得全局的计数结果。G-Counter 是一个只增长的计数器,它满足如下的性质:
PN-CounterPN-Counter 是一种基于 CRDT 的数据类型,用于实现分布式环境中的计数器功能。由两个 G-Counter 组成的数据结构,分别用于记录正数和负数的计数。每个副本都维护自己的两个计数器,分别用于增加正数计数和增加负数计数。当需要获取计数时,副本会将正数计数器的值减去负数计数器的值,以获得全局的计数结果。PN-Counter 具有以下性质:
Sequence CRDTSequence CRDT 用于实现分布式环境中的有序序列功能,旨在解决在并发环境中对有序序列进行并发操作时可能出现的冲突问题。它允许并发操作在不同副本之间交换和合并,以达到最终一致性的状态。Sequence CRDT 的实现方式有多种,其中一种常见的实现是基于标识符(Identifier)的方式。每个操作都被赋予唯一的标识符,用于标识操作的顺序。常见的操作包括插入元素、删除元素和移动元素。通过使用标识符和一致的合并策略,Sequence CRDT 可以实现在分布式环境中对有序序列进行并发操作的正确合并。具体的合并策略可以根据应用需求和具体实现进行定制。Sequence CRDT 具有以下特性:
Sequence CRDT 可以应用于许多场景,如协同编辑、版本控制系统、聊天应用等,其中有序的操作是必要的。它提供了在分布式环境中实现有序序列的能力,并保持最终一致性。 基于状态的 CRDT(CvRDT)与基于操作的 CRDT(CmRDT)不同,基于状态的 CRDT(也称为收敛复制数据类型,CvRDT)通过将完整的本地状态发送到其他副本来进行状态传播。在 CvRDT 中,副本接收到完整的状态并将其与自身的状态合并。合并函数必须满足可交换性、结合性和幂等性,确保副本之间的合并结果是相同的。 这意味着合并操作的顺序不影响最终结果,并且即使多次合并相同的状态,结果也不会发生变化。所有副本的状态都可以通过合并来收敛到同一个最终状态。为了确保一致性,更新函数必须遵循一个偏序规则,使得每次合并都能够单调地增加内部状态。 Delta 状态 CRDT 是基于状态的 CRDT 的一种优化版本。在 Delta CRDT 中,仅传播最近对状态进行的更改(即"delta"),而不是将整个状态传输到其他副本。这减少了每次更新的网络开销,并提高了效率。只有当某个副本的状态发生变化时,才会将该变化广播给其他副本,从而避免了大量不必要的数据传输。 G-SetG-Set 是一种基于 CRDT 的数据类型,用于实现分布式环境中的集合功能,G-Set 是一个只增长的集合,每个副本都维护自己的本地集合。当需要添加元素时,副本只会在自己的集合中添加元素,而不会删除或修改其他副本的集合。G-Set 的特性包括:
由于 G-Set 是只增长的集合,它满足最终一致性和合并性质。每个副本的本地集合可以独立地增长,不会发生冲突或合并操作。当需要获取全局集合时,可以简单地将所有副本的集合合并。G-Set 适用于需要在分布式环境中维护集合,并且可以实现高可用性和最终一致性的场景。它常用于记录一组唯一的元素,而不需要删除或修改元素 2P-Set2P-Set 用于实现分布式环境中的集合功能,维护两个集合:一个"添加集合"和一个"移除集合"。每个元素在添加集合中只能添加一次,在移除集合中只能移除一次。这样,2P-Set 可以实现添加和移除元素的操作,并且确保元素不会重复添加或移除。2P-Set 的操作包括:
2P-Set 的特性包括:
当需要获取全局集合时,副本将所有副本的添加集合和移除集合合并,并计算添加集合减去移除集合的结果,得到最终的全局集合。2P-Set 可以实现在分布式环境中维护集合,并且具有最终一致性。它适用于需要记录添加和移除操作,并且不希望元素重复添加或移除的场景。 LWW-Element-SetLWW-Element-Set 用于实现分布式环境中的集合功能,维护一个集合,其中每个元素都与一个时间戳相关联。时间戳可以是递增的整数,逻辑时钟,或其他可比较的时间表示。每当需要添加或移除元素时,副本会将元素与当前时间戳关联,并将操作应用到本地集合。LWW-Element-Set 的特性包括:
当需要获取全局集合时,副本将所有副本的集合合并,并根据时间戳选择最新的操作。如果存在多个副本对同一个元素进行了不同的操作,那么具有较新时间戳的操作将覆盖较旧时间戳的操作。LWW-Element-Set 可以实现在分布式环境中维护集合,并且具有最终一致性。它适用于需要记录元素的添加和移除,并以最后写操作为准的场景。然而,由于最后写操作胜出的特性,可能会导致某些操作的冲突或覆盖 OR-SetOR-Set 用于实现分布式环境中的集合功能,维护一个集合,其中每个元素都与一个标识符相关联。当需要添加元素时,副本会为元素生成一个唯一的标识符,并将其添加到本地集合中。当需要移除元素时,副本会为要移除的元素生成一个移除标记,并将其关联到原始元素的标识符上。OR-Set 的特性包括:
当需要获取全局集合时,副本将所有副本的集合合并,并根据标识符和移除标记进行操作。如果一个元素的标识符存在于集合中,但它的移除标记也存在,则该元素被视为已移除。这样,移除操作具有优先级高于添加操作的效果。OR-Set 可以实现在分布式环境中维护集合,并且具有最终一致性。它适用于需要记录元素的添加和移除,并且允许移除操作覆盖添加操作的场景。 CmRDTs 和 CvRDTs相比于 CvRDTs,CmRDTs 在副本之间传输操作的协议上有更多要求,但当事务数量相对于内部状态的大小较小时,它们使用的带宽较少。然而,由于 CvRDT 的合并函数是可结合的,与某个副本的状态进行合并会包含该副本的所有先前更新。在减少网络使用和处理拓扑变化方面,使用 Gossip 协议可以很好地传播 CvRDT 状态到其他副本。 CRDT 的数学基础CRDT 的核心在于其合并操作必须满足一组特定的数学性质,这些性质保证了在分布式系统中数据最终能够达到一致。合并操作(通常表示为 ∨)必须满足以下三个关键性质: 1. 交换律(Commutativity)合并操作的顺序不影响最终结果: [ A \vee B = B \vee A ] 这意味着无论是节点 A 先将数据同步给节点 B,还是节点 B 先将数据同步给节点 A,最终的结果都是一样的。这个性质对于分布式系统特别重要,因为在实际环境中,我们无法保证数据同步的顺序。 示例:
2. 结合律(Associativity)多个合并操作的顺序不影响最终结果: [ (A \vee B) \vee C = A \vee (B \vee C) ] 这个性质确保了在有多个节点时,无论按什么顺序进行合并,最终结果都是一致的。这对于大规模分布式系统尤为重要,因为数据同步可能涉及多个节点的链式传递。 示例:
3. 幂等律(Idempotency)重复合并不会改变结果: [ A \vee A = A ] 这个性质保证了即使同一个更新被应用多次(例如由于网络问题导致的重复传输),也不会影响最终状态。这对于构建容错的分布式系统至关重要。 示例:
实际意义这些数学性质的重要性体现在:
在实际应用中,这些性质使得 CRDT 特别适合构建:
通过确保这些数学性质,CRDT 能够在不需要复杂的协调机制的情况下,保证分布式系统中数据的最终一致性。 CRDT 是如何处理冲突的下图描述了 Yjs 中处理冲突的算法模型,它是一个支持点对点传输的冲突处理模型。 首先我们先来解释一下图中的元素:
图示的操作顺序:
当两个操作发生并发冲突(例如 在这个例子中,用户 1 的标识符(1)大于用户 0 的标识符(0),因此生成的最终文档顺序是 CRDT 机制能够避免传统操作转发(OT)所面临的冲突问题,同时保证最终一致性,原因在于其设计采用了冲突自由的合并规则,而不依赖于复杂的操作变换和中央协调。 在 OT 中,当多个用户并发地对同一数据进行操作时,系统需要通过操作转发和变换来确保操作顺序的一致性。这通常涉及复杂的变换逻辑,例如在两个用户同时修改相同数据位置时,OT 会通过变换算法调整其中一个操作的位置或内容,以确保最终结果一致。尽管 OT 可以解决许多并发冲突,但这种变换机制本身具有高复杂性,特别是在多个用户同时进行操作时,操作的变换和冲突解决可能导致性能瓶颈、维护困难,以及在极端情况下可能产生不一致的结果。 与此不同,CRDT 通过设计内建的合并规则来避免这些问题。每个 CRDT 数据结构都确保其操作是幂等、交换性强且结合性好的,这意味着无论操作顺序如何或是否发生并发操作,所有副本都能够自动且无冲突地合并,最终收敛到一致的状态。CRDT 不依赖于操作的顺序或中央协调,而是依靠每个操作的唯一标识符和局部合并规则来直接解决并发冲突,从而显著减少了在处理冲突时的计算复杂度。 此外,CRDT 的这一机制使得它天然适合高可用性和容错性要求较高的分布式系统,在面对网络分区、节点故障等场景时,系统依然能够继续操作并保证数据一致性。因此,CRDT 更加简洁、易于扩展,并能够在没有显式操作转发和变换的情况下,确保最终一致性,从根本上避免了 OT 中因操作顺序和变换导致的复杂性和潜在冲突。 CRDT 如何解决脏路径问题在分布式系统中,脏路径(Dirty Path)问题通常出现在多个副本之间进行并发操作时,导致副本之间的数据状态不一致。由于不同副本的操作可能由于网络延迟、分区或同步问题而不同步,这使得系统中可能出现不一致的数据状态。传统的分布式系统通常依赖中心化的协调机制来同步数据,但这也容易引发性能瓶颈和复杂的冲突解决问题。CRDT(冲突自由复制数据类型)通过去中心化和无冲突的操作设计,避免了脏路径问题,确保多个副本能够在并发操作后最终收敛到一致的状态。 以下是 CRDT 如何通过一系列设计原则来解决脏路径问题的详细过程: 1. 唯一标识符与操作标记CRDT 使用唯一标识符来区分每个操作,每个操作的标识符通常由 用户标识符(例如用户 ID)和 操作序列号(通常是时间戳或递增的操作编号)组成。唯一标识符保证了操作的顺序,即使这些操作在不同副本上并发发生。 操作标识符的作用:
这种设计避免了因操作没有明确顺序而产生的不一致或冲突,从而有效地避免了脏路径问题。 示例:假设用户 A 在副本 1 上插入了一个字符 2. 并发操作的解决在 CRDT 中,每个副本都能够独立进行操作,当多个副本发生并发操作时,CRDT 使用设计的 合并规则 来自动解决冲突,确保所有副本最终达到一致状态。 如何处理并发操作?
例如,假设 3. 合并规则与最终一致性CRDT 的设计关键在于 合并规则,即如何将并发操作合并为一致的状态。这些合并规则确保了即使副本之间的操作顺序不同,最终副本的数据会收敛到相同的状态。 合并规则保证一致性:
这些规则使得 CRDT 在面对并发更新时,能够自动解决冲突并收敛到一致的状态。 举例说明:假设两个用户并发进行插入操作,用户 A 在副本 1 中插入 4. 双向链表在 CRDT 中的应用在一些 CRDT 应用(例如文本编辑器)中,双向链表 被用来存储数据。双向链表的结构非常适合表示具有顺序关系的数据,并且支持高效的插入、删除和更新操作。 双向链表如何解决脏路径问题:
通过这种方式,CRDT 可以处理并发插入、删除操作,避免因操作顺序不同而引发脏路径问题。 5. 最终一致性CRDT 通过合并规则确保所有副本最终一致。即使操作在不同副本之间发生延迟或顺序不同,最终的合并结果会保证一致性。 如何确保最终一致性?
通过最终一致性,CRDT 确保即使在网络分区或节点故障的情况下,系统中的所有副本最终都会收敛到相同的数据状态,避免了脏路径问题。 6. 避免脏路径:总结CRDT 解决脏路径问题的关键在于:
通过这些机制,CRDT 确保了分布式系统中的高可用性、容错性和一致性,避免了脏路径问题的出现,并且简化了分布式系统中并发操作的管理。 CRDT 如何解决 UNDO/REDO 问题在分布式系统中,UNDO 和 REDO 是常见的操作需求,尤其是在分布式应用(如分布式文本编辑器、协作平台等)中,这些操作通常需要确保数据的一致性和正确的操作回溯。然而,传统的事务日志和操作转发(OT)机制在处理这些操作时可能会遇到同步、顺序和冲突等问题。而 CRDT(冲突自由复制数据类型)通过其特有的设计原则,能够优雅地解决 UNDO 和 REDO 问题,保证分布式系统中操作的回滚与重做能够在多个副本间一致地执行。 什么是 UNDO 和 REDO?
在分布式系统中,UNDO 和 REDO 需要跨多个副本同步,以保证每个副本中的历史操作可以一致地回滚或重做。此过程可能会受到以下问题的影响:
CRDT 如何解决 UNDO 和 REDO 问题CRDT 提供了一些特性,使其特别适合解决 UNDO 和 REDO 问题,尤其是在分布式环境下。这些特性包括 冲突自由的操作合并、幂等性、交换性、结合性、以及 最终一致性。通过这些特性,CRDT 可以处理操作回滚和重做时遇到的挑战。 1. 操作历史与逆向操作(Undo/Redo)CRDT 中的每个操作(如插入、删除等)都有一个唯一标识符。通过设计合适的操作历史结构,CRDT 可以存储每个操作,并支持操作的回溯和重做。这对于分布式系统中的 UNDO 和 REDO 操作至关重要。 操作的存储和标识:
操作回滚(UNDO):
操作重做(REDO):
2. 如何支持并发和冲突解决在分布式系统中,UNDO 和 REDO 操作通常是在多个副本之间执行的,可能会遇到并发冲突的问题。CRDT 的核心特性能够有效地解决并发冲突问题,从而确保 UNDO 和 REDO 操作的一致性。 幂等性、交换性和结合性:
这些特性使得 CRDT 在多个副本上执行 UNDO 和 REDO 操作时,可以自动解决并发冲突,确保不同副本的数据始终一致。 解决并发冲突的方式:
示例:
3. 最终一致性与操作回溯CRDT 的设计目标之一是 最终一致性。即使操作的执行顺序不同,所有副本最终都会达到一致的状态。对于 UNDO 和 REDO 操作,CRDT 确保它们的执行不会破坏最终一致性。 确保一致性:
4. 双向链表的应用在一些具体的 CRDT 实现中(例如分布式文本编辑器),使用 双向链表 来存储数据,这使得 UNDO 和 REDO 操作更容易实现。 双向链表支持操作回溯:
双向链表使得撤销和重做操作在数据结构中非常高效,并且能够根据唯一标识符和合并规则来正确解决冲突。 CRDT 通过以下几个关键特性解决了 UNDO 和 REDO 问题:
通过这些机制,CRDT 在分布式环境下不仅保证了 UNDO 和 REDO 的一致性,还有效解决了并发冲突和操作历史同步的问题。 CRDT 解决并发冲突接下来我们将以图片设置 align 属性为例介绍,首先看看 CRDT 如何描述对象属性及属性修改: 左边是图片数据模型,右边是模拟 CRDT 对应的数据结构,图片对象中的每一个字段都使用结构对象去描述内容及内容的修改,这里以 align 字段的代表看它的表达 操作 1️⃣: 左边是图片数据模型,右边是模拟 CRDT 对应的数据结构,图片对象中的每一个字段都使用结构对象去描述内容及内容的修改,这里以 align 字段的代表看它的表达 图中最上方的蓝色结构表示 随后,用户执行了操作 ①,将 ⚠️ 值得注意的是:此结构中的
为避免混淆,请理解:结构对象中间的那一块,才是真正表示属性值的内容,而两侧的 操作 2️⃣: 当然!以下是你后续“并发修改”部分的润色版本,紧接在“顺序修改”之后,风格统一,逻辑清晰,读起来也更专业: 与前面的顺序修改不同,在并发场景中,多个用户几乎同时基于相同的状态进行修改操作。此时,CRDT 会采用特定的合并策略来决定各个操作的插入顺序,从而确保所有副本最终达成一致。 如图所示,这一次有两个用户同时基于状态
由于这两个操作是并发的,它们都指向相同的前置节点 最终形成的双向链表结构如下:
系统将 这种机制展现了 CRDT 在面对并发修改时的优势:无需冲突检测,也不丢失任一修改历史,并能通过一致的排序规则达成最终一致性。 下面看看两个用户并发的执行属性修改时产生的数据结构: 与前面的顺序操作不同,此处执行的操作 ① 和操作 ② 是并发修改:它们都是基于同一个前置状态,即 具体来说:
由于两个修改操作的基础状态相同,因此构成并发。在这种情况下,CRDT 会根据标识符的全局有序性来进行合并处理。 在本示例中,
最终形成如下链表结构:
因此,最终 这一过程体现了 CRDT 对并发操作的自动合并能力:无需人工干预或冲突解决策略,仅通过标识符排序,就能实现一致性和可预期的合并结果。 顺序修改 vs 并发修改:对比总结
在 CRDT 模型下,无论是顺序修改还是并发修改,都能通过结构化的数据表示 + 有序标识符来安全地整合操作,确保最终状态一致,并完整保留修改轨迹。这正是 CRDT 在协同编辑、离线同步等场景下强大而可靠的基础。 参考文章总结CRDT(无冲突复制数据类型)是一类用于分布式系统中的数据结构,它通过内建的幂等性、交换性和结合性操作,支持各副本在无协调情况下独立更新并自动合并,最终收敛为一致状态。它避免了传统并发控制中对冲突的显式处理,适用于离线编辑、多端同步、协同操作等高可用场景。通过唯一标识符和结构化合并策略,CRDT 能在面对并发修改、网络分区等挑战时保持数据一致性和操作完整性。 转自https://juejin.cn/post/7490425439757664271 该文章在 2025/4/14 9:58:58 编辑过 |
关键字查询
相关文章
正在查询... |