CRDT宝典 - LWWRegister

背景

在分布式系统中,有一个值、以及一个时间戳,每个节点都能更新这个值,同时更新这个时间戳。

请设计一个CRDT(Conflict-free Replicated Data Type)来实现一个满足最终一致性的分布式系统。

为了减少要理解的概念,下文描述的CRDT同时有两层意思

  1. 无冲突的数据类型,即类型
  2. 一个CRDT实例,即实例

思维链

flowchart A["1. CRDT可以应用在不同的节点中"] A --> B["2. CRDT可以表示该值的最终值、以及时间戳的最终值"] B --> C["3. CRDT可以让任意节点更新这个值和时间戳"] C --> D["4. 可以合并多个节点的CRDT,且无论合并的顺序是怎么样的,其结果都一致,\n即Merge(A.CRDT, B.CRDT, C.CRDT) = Merge(B.CRDT, C.CRDT, A.CRDT) = a new CRDT"] D --> E["5. 多次合并同一个节点的CRDT,其结果都等于合并一次,\n即Merge(A.CRDT, B.CRDT, B.CRDT) = Merge(A.CRDT, B.CRDT) = a new CRDT"]

特别说明:第二点的最终值代表应用完所有节点对该值的更改后的最终值,时间戳同样。

实现

这个CRDT如下,我们暂时给它起名LWWRegister(稍后解释其为什么叫LWWRegister)

ts 复制代码
type LWWRegister<T> = {
    value: T;
    timestamp: number;
};

因为是分布式系统,且支持弱网环境,所以这个LWWRegister在不同节点里,都不一样。 比如

ts 复制代码
// 节点A的LWWRegister
A.LWWRegister = {
    value: "value1",
    timestamp: 1000,
}
// 节点B的LWWRegister
B.LWWRegister = {
    value: "value2",
    timestamp: 1500,
}
// 节点C的LWWRegister
C.LWWRegister = {
    value: "value3",
    timestamp: 1200,
}

各自节点可以随时更新自己的值和时间戳,比如在节点A中,更新LWWRegister

ts 复制代码
const update = (node, newValue: string, newTimestamp: number) => {
    node.LWWRegister = {
        value: newValue,
        timestamp: newTimestamp,
    };
};

合并不同节点的副本,比如merge(A.LWWRegister, B.LWWRegister),需要根据时间戳选择最新的值,步骤如下

flowchart LR B["1.创建一个LWWRegister\n 2. 比较A.LWWRegister\n和B.LWWRegister的timestamp"] B1["2.1 如果A.LWWRegister.timestamp > B.LWWRegister.timestamp,\n则选A.LWWRegister的值和timestamp"] B2["2.2 如果B.LWWRegister.timestamp > A.LWWRegister.timestamp,\n则选B.LWWRegister的值和timestamp"] B3["2.3 如果A.LWWRegister.timestamp === B.LWWRegister.timestamp,\n则按照预定义的规则(例如节点ID大小)选择值"] C["3. 返回选择后的LWWRegister"] B --> B1 B --> B2 B --> B3 B1 --> C B2 --> C B3 --> C

代码如下:

ts 复制代码
const merge = (regA: LWWRegister<string>, regB: LWWRegister<string>): LWWRegister<string> => {
    if (regA.timestamp > regB.timestamp) {
        return regA;
    } else if (regB.timestamp > regA.timestamp) {
        return regB;
    } else {
        // 当时间戳相同时,使用节点ID进行比较,这里假设A的ID小于B
        return /* 根据节点ID决定返回哪个 */;
    }
}

const mergedRegister = merge(A.LWWRegister, B.LWWRegister)

你按随意的顺序合并A.LWWRegister,B.LWWRegister,C.LWWRegister,其结果都一致,即最终一致性。

总结

因为这个CRDT通过时间戳来解决冲突,所以叫LWWRegister(Last-Write-Wins Register)

相关推荐
智者知已应修善业6 小时前
【求中位数】2024-1-23
c语言·c++·经验分享·笔记·算法
地平线开发者6 小时前
PTQ 量化数值范围与优化
算法·自动驾驶
sali-tec6 小时前
C# 基于halcon的视觉工作流-章68 深度学习-对象检测
开发语言·算法·计算机视觉·重构·c#
测试人社区-小明7 小时前
智能弹性伸缩算法在测试环境中的实践与验证
人工智能·测试工具·算法·机器学习·金融·机器人·量子计算
天天扭码7 小时前
如何实现流式输出?一篇文章手把手教你!
前端·aigc·ai编程
前端 贾公子7 小时前
vue移动端适配方案 === postcss-px-to-viewport
前端·javascript·html
罗西的思考7 小时前
【Agent】MemOS 源码笔记---(5)---记忆分类
人工智能·深度学习·算法
GISer_Jing8 小时前
AI营销增长:4大核心能力+前端落地指南
前端·javascript·人工智能
明远湖之鱼9 小时前
一种基于 Service Worker 的渐进式渲染方案的基本原理
前端