CRDT宝典 - Delta-state AWOSet

背景

看完AWORSet,它有一个致命的缺点:元信息很大,大到远超数据本身,比如

ts 复制代码
A.AWORSet = [
    {
        'element1': {
            [A.id]: 1,
            [B.id]: 2,
            [C.id]: 5,
            [D.id]: 1,
            [E.id]: 8,
            [F.id]: 3,
            [G.id]: 7,
            [H.id]: 4,
            [I.id]: 9,
            [J.id]: 2,
            [K.id]: 6,
            [L.id]: 5,
        },
    },
    {
    }
]

我们为了表示出不同节点添加element1的操作,其元数据本身远远超过了element1本身,这是我们不能容忍的,这会导致以下的问题

  1. 不同节点的CRDT需要将自身的整个CRDT推送到远端用于合并,这数据量太大了
  2. 合并CRDT时,耗时太长了

我们需要一个减少元数据的方式

思维链

flowchart A["1. 每个节点都会间隔性地拉取远端节点的AWORSet,然后和自身的AWORSet进行合并,\n合并后的结果会覆盖掉原来的AWORSet。\n同样也会间隔性的将自身的AWORSet推送到远端节点,用于同步。"] A --> B["2. 同时,节点自身会产生许多对AWORSet的操作。\n`记住,这些操作只代表该节点对AWORSet的操作`"] B --> C["3. 我们聚焦节点A,其实它只需要将自身对AWORSet产生的增删操作同步出去就可以了,\n我们完全可以用delta来表达这些操作"] C --> D["4. 很明显,delta = `添加集` + `删除集` = 一个AWORSet,\n所以将delta合并到AWORSet中 = 两个AWORSet的合并"] D --> E["5. 通过delta的方式,我们能很显著的解决`背景`里提出的两个问题"]

实现

这个CRDT如下,我们称之为Delta-state-AWORSet,我们简称一下:Delta-AWORSet

ts 复制代码
A.Delta-AWORSet = {
    values: [
        {
            'element1': {
                [A.id]: 3,
                [B.id]: 2
            },
            'element3': {
                [A.id]: 1,
                [B.id]: 2
            },
        },
        {
            'element1': {
                [A.id]: 3,
                [B.id]: 2
            },
        }
    ],
    delta:[
        {
            'element1': {
                [A.id]: 3,
                [B.id]: 2
            },
        },
        {
            'element2': {
                [A.id]: 1,
            },
        }
    ]
}

代表节点从上一次同步delta后,进行了新增element1删除element2的操作

某个节点添加元素,比如A节点添加了element1,即A.Delta-AWORSet.delta.add(element1),并且A.Delta-AWORSet.values.add(element1)

某个节点删除元素,比如A节点删除了element2,即A.Delta-AWORSet.delta.remove(element2),并且A.Delta-AWORSet.values.remove(element2)

某个节点的Delta-AWORSet的值,是A.Delta-AWORSet.values.value()

同步其他节点的Delta-AWORSet,比如将B节点的Delta合并到A节点中,只需要将A.Delta-AWORSet.values.merge(B.Delta-AWORSet.delta)

合并两个Delta-AWORSet,比如合并A.Delta-AWORSet和B.Delta-AWORSet,只需要将A.Delta-AWORSet.values.merge(B.Delta-AWORSet.values),且A.Delta-AWORSet.delta.merge(B.Delta-AWORSet.delta)

代码如下(伪代码):

ts 复制代码
A.Delta-AWORSet:<{values: AWORSet, delta: AWORSet}> = {
    values: [
        {
            'element1': {
                [A.id]: 3,
                [B.id]: 2
            },
            'element3': {
                [A.id]: 1,
                [B.id]: 2
            },
        },
        {
            'element1': {
                [A.id]: 3,
                [B.id]: 2
            },
        }
    ],
    delta:[
        {
            'element1': {
                [A.id]: 3,
                [B.id]: 2
            },
        },
        {
            'element2': {
                [A.id]: 1,
            },
        }
    ]
}

// 添加元素的代码示例
function addElement(node: Delta-AWORSet, element) {
    node.values.add(element);
    node.delta.add(element);
}

// 删除元素的代码示例
function removeElement(node: Delta-AWORSet, element) {
    node.values.remove(element);
    node.delta.remove(element);
}

// 值的获取
function getValue(node: Delta-AWORSet) {
    return node.values.value();
}

// 同步其他节点的Delta
function mergeDelta(node: Delta-AWORSet, delta: Delta-AWORSet) {
    node.values.merge(delta.values);
}

// 合并两个Delta-AWORSet的代码示例
function mergeDelta(node: Delta-AWORSet, delta: Delta-AWORSet) {
    node.values.merge(delta.values);
    node.delta.merge(delta.delta);
}

QA

问:都有delta了,为什么还需要合并两个Delta-AWORSet?

答:因为你业务决定的,可能会存在需要全量同步的需求。

问:为什么合并别的节点的delta的操作里,只需要合并values,不需要合并delta?

答:你问到delta的精髓了,A.Delta-AWORSet.delta代表着A节点对Set的操作,而这个操作绝对是最新的(无论是离线还是在线,且别的节点里不可能有比这个delta还新的操作,因为A节点是A.Delta-AWORSet.delta的唯一产生者),也就算说即便A.Delta-AWORSet.values合并了别的节点的delta,A.Delta-AWORSet.delta里的操作也能保证是最新的。

问:什么时候delta可以重制

答:看业务,比如A节点将自身的delta同步出去后就可以重制delta了,因为这代表着A对Set的操作都同步出去了,没必要保留delta了。对,所以delta完全没必要在硬盘中,它可以在内存中(性能又高了一截)。

总结

很复杂,需要看好几遍,有问题的评论区见

相关推荐
辰烨chenye2 小时前
LeetCode Hot 100 题解 · 普通数组篇
算法·leetcode·职场和发展
mldong3 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排4 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60614 小时前
如何自己造一个时间处理库
前端·javascript·github
hqyjzsb5 小时前
零 AI 项目经验,学 Python 转型 AI 的正确顺序是什么?
开发语言·人工智能·python·算法·职场和发展·数据挖掘·数据分析
辰烨chenye5 小时前
LeetCode Hot 100 题解 · 二分篇
java·算法·leetcode
微功夫信息技术5 小时前
分层多智能体强化学习驱动的非急救转运公平 - 效率统一调度系统研究与实践
人工智能·学习·算法·动态规划
IT大白鼠5 小时前
MySQL 分布式集群系列 · 第四篇——实操部署指南:从零搭建生产级 MySQL NDB 集群
数据库·分布式·mysql
IT大白鼠5 小时前
MySQL 分布式集群系列 · 第三篇——核心原理精讲:NDB 自动分片、多主写入与数据同步
数据库·分布式·mysql
走到天涯海角6 小时前
react里面的长列表渲染优化
前端·react.js·前端框架