云开发数据库里规避写覆盖

云开发数据库不提供锁接口,实际使用的时候又经常要被多个实例并发操作,那要想要规避写覆盖往往只能通过乐观锁,但是这又会带来附加的query操作。那能不能不用任何锁就规避写覆盖呢?其实也不是不行。

一个业务偶然遇到了并发写操作相互覆盖的问题。直觉的想法是能不能加锁?看了一下云开发数据库没有提供锁的接口(当然数据库自己写操作的时候肯定实现了自己的锁)。

那很自然的就想到了,用inc操作维护记录的版本号,加乐观锁来避免误写。不过这样的代价也很大,update操作不能用快速的doc操作来定位记录,而必须用条件查询方式来定位记录,并发写的时候还有可能需要进行多次的重试获取版本并写入直到自己排上队,那要重试几次合适呢?还是得考虑下怎么利用数据库自己的锁机制来避免覆盖。

一个解决办法是把数据放到数组里面,更新数据的时候用push或者unshift来插入数据,这样即使并发写也不会相互写覆盖,但是如果是相同的数据重复写入的话可能数组里面出现重复元素的问题,可能需要处理额外的去重逻辑。

如果想要自动去重,也可以用哈希对象来管理要写入的数据(数据写到key,value如果没有特别需要可以写true或者1之类的简单类型。),然后在用update语句的字段set能力来实现并发更新记录的时候相互不覆盖:

1
2
3
4
5
function test(i){
var answer={};
answer["answer"+i] = _.set(1);
return coll.doc(uid).update({answer:answer})
}

尝试一下效果:

1
2
3
4
5
6
7
8
9
var n=100,p=[];
for(var i=0;i<n;i++){
p.push(test(i))
}
Promise.all(p).then(res=>{
coll.doc(uid).get().then(res=>{
console.log(JSON.stringify(res));
})
})

如果重复的key写入相同的数据,会得到{updated:0}的结果,并不会影响已经写入的数据。

当然如果需要进一步处理重复写入逻辑,也可以吧_.set(1) 改成 _.inc(1) 来记录同一个key被重复写入了多少次。

要留意的是并发数n过大的时候会出现超时。