我们知道Firebase没有计数查询,计数器的方法是两个,在客户端获取所有数据或在数据库中存储计数值.我使用第二种方法,因为第一种方法仅在数据很少时才有效.
这个方法的问题是我可以有很多并发编写器,我用我的方法解决了这个问题,如果没有存储数据,它会尝试用递增或递减的计数器重新保存数据.
好吧,当我想只改变一个计数器时,这种方法效果很好,但在不同的情况下,我想用一个事务更改许多不同的计数器,在这种模式下,存在一种风险,即在不同的尝试后数据无法保存,因为我们可以在许多不同的节点中有许多并发写入器.具有原子更新的数据结构示例如下:
如果我保存这个
"comment": {
"counter": {},
"Data": {
"$Comment":{}
}
}
comment/counter = val + 1
comment/Data/comment1 = data
它是有效的,如果我无法更新,因为其他编写器正在更新计数器时,我可以重新使用新的计数器值,通常是在我第一次成功结束操作后,但是,例如,如果我想删除用户和所有他的行为,我想存储这种类型的数据
"comment":{
"counter":{},
"Data": {
"$Comment":{}
}
},
"$user": {
"follower":{
"counter":{},
"data":{
"$Follower": {}
}
}
}
comment/counter = val - 1
comment/Data/comment2 = null
user1/follower/counter = val - 1
user1/follower/data/user30 = null
user2/follower/counter = val - 1
user2/follower/data/user30 = null
user3/follower/counter = val - 1
user3/follower/data/user30 = null
可见,在批量更新的情况下,我必须更新许多可以拥有大量并发写入器的计数器,在这种情况下,很容易在不同的重试之后无法更新所有计数器.
另一种情况是这样的
"photo":{
"$photo":{
"comment":{
"counter":{},
"Data": {
"$Comment":{}
}
}
}
photo/10/comment/counter = val - 1
photo/10/comment/Data/comment1 = null
photo/10/comment/counter = val - 1
photo/10/comment/Data/comment2 = null
photo/10/comment/counter = val - 1
photo/10/comment/Data/comment3 = null
在这种情况下,无法更新计数器,因为我尝试删除具有相同计数器的不同注释,只能等于值1或值-1.如果我想删除所有用户数据,则可能是他写的同一张照片中的一些评论但我不能在一个交易中删除所有这些评论,因为计数器只能通过单个交易进行更新.
我看到很多这样的并发症只是因为不存在任何类型的数据库(sql或nosql)中存在的计数查询.我不懂为什么.这涉及许多问题,否则可以解决而不浪费时间
下面是我更新计数器的javascript函数.这是一个递归函数,如果由于其他并发编写器而导致更新失败,则会回忆自己
this.singleUpdate = function (paths, increment, countPath, retryCounter, callback) {
firebase.database().ref(countPath).once('value', function (counter) {
var value = counter.val() ? counter.val().value : 0;
var countValue = increment ? value + 1 : value - 1;
paths[countPath] = { value: null };
if (!increment && countValue == 0) { paths[countPath] = null; }
else {
paths[countPath].value = countValue;
}
firebase.database().ref().update(paths, function (error) {
if (error) {
retryCounter++;
if (retryCounter < 3) { singleUpdate(paths, increment, countPath, retryCounter, callback); }
else { callback(false, error); }
} else {
return callback(true, countValue);
}
});
});
}
解决方法:
你在寻找transactions,不是吗?
When working with data that could be corrupted by concurrent
modifications, such as incremental counters, you can use a transaction
operation. You can give this operation an update function and an
optional completion callback. The update function takes the current
state of the data as an argument and returns the new desired state you
would like to write. If another client writes to the location before
your new value is successfully written, your update function is called
again with the new current value, and the write is retried.
来自docs的示例:
function toggleStar(postRef, uid) {
postRef.transaction(function(post) {
if (post) {
if (post.stars && post.stars[uid]) {
post.starCount--;
post.stars[uid] = null;
} else {
post.starCount++;
if (!post.stars) {
post.stars = {};
}
post.stars[uid] = true;
}
}
return post;
});
}
另外,您只能删除已删除用户发布的内容,因此计数器将保持相同的值,而不是评论/帖子将有“帖子被删除”
原文地址:https://codeday.me/bug/20190711/1429326.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。