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)

相关推荐
0思必得04 小时前
[Web自动化] Selenium处理动态网页
前端·爬虫·python·selenium·自动化
-dzk-4 小时前
【代码随想录】LC 59.螺旋矩阵 II
c++·线性代数·算法·矩阵·模拟
风筝在晴天搁浅4 小时前
hot100 78.子集
java·算法
Jasmine_llq4 小时前
《P4587 [FJOI2016] 神秘数》
算法·倍增思想·稀疏表(st 表)·前缀和数组(解决静态区间和查询·st表核心实现高效预处理和查询·预处理优化(提前计算所需信息·快速io提升大规模数据读写效率
超级大只老咪4 小时前
快速进制转换
笔记·算法
东东5164 小时前
智能社区管理系统的设计与实现ssm+vue
前端·javascript·vue.js·毕业设计·毕设
catino4 小时前
图片、文件的预览
前端·javascript
m0_706653234 小时前
C++编译期数组操作
开发语言·c++·算法
故事和你915 小时前
sdut-Java面向对象-06 继承和多态、抽象类和接口(函数题:10-18题)
java·开发语言·算法·面向对象·基础语法·继承和多态·抽象类和接口
qq_423233905 小时前
C++与Python混合编程实战
开发语言·c++·算法