- CRDT宝典(一): 引言
- CRDT宝典(二): 基本概念
- CRDT宝典(三): GCounter
- CRDT宝典(四): PNCounter
- CRDT宝典(五): GSet
- CRDT宝典(六): PNSet
- CRDT宝典(七): VClock
- CRDT宝典(八): LLW-Register
- CRDT宝典(九): ORSet
- CRDT宝典(十): AWORSet
- CRDT宝典(十一): Delta-state-AWORSet
背景
看完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本身,这是我们不能容忍的,这会导致以下的问题
- 不同节点的CRDT需要将自身的整个CRDT推送到远端用于合并,这数据量太大了
- 合并CRDT时,耗时太长了
我们需要一个减少元数据
的方式
思维链
实现
这个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完全没必要在硬盘中,它可以在内存中(性能又高了一截)。
总结
很复杂,需要看好几遍,有问题的评论区见