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)

相关推荐
刘发财4 小时前
弃用html2pdf.js,这个html转pdf方案能力是它的几十倍
前端·javascript·github
牛奶7 小时前
2026年大模型怎么选?前端人实用对比
前端·人工智能·ai编程
牛奶7 小时前
前端人为什么要学AI?
前端·人工智能·ai编程
地平线开发者9 小时前
SparseDrive 模型导出与性能优化实战
算法·自动驾驶
董董灿是个攻城狮9 小时前
大模型连载2:初步认识 tokenizer 的过程
算法
Kagol10 小时前
🎉OpenTiny NEXT-SDK 重磅发布:四步把你的前端应用变成智能应用!
前端·开源·agent
地平线开发者10 小时前
地平线 VP 接口工程实践(一):hbVPRoiResize 接口功能、使用约束与典型问题总结
算法·自动驾驶
罗西的思考10 小时前
AI Agent框架探秘:拆解 OpenHands(10)--- Runtime
人工智能·算法·机器学习
GIS之路11 小时前
ArcGIS Pro 中的 notebook 初识
前端
JavaGuide11 小时前
7 道 RAG 基础概念知识点/面试题总结
前端·后端