如何在Firebase Realtime Database中实现Sequence/计数器结构并解决并发问题
static Future<int> get nextId async { return database.get().then((snapshot) { return snapshot.value; }).then((value) { database.set(value + 1); return value + 1; }); }
结论
你当前的实现逻辑在多用户并发调用时,必然存在获取到相同返回值的风险。
风险产生的原因
- 这段逻辑属于非原子的「读-改-写」操作序列,读取、修改、写入是三个完全独立的步骤,且步骤之间没有任何并发安全保障
- 并发场景下会出现如下异常时序:
- 用户1调用
nextId,首先通过database.get()读取到当前值为10 - 还没等用户1执行
database.set(11)操作,用户2也调用了nextId,同样读取到当前值为10 - 两个用户后续都会执行写入
11的操作,且最终都返回11,出现ID重复
- 用户1调用
修复方案
你需要避免自行拆分读、写操作,优先使用你所用NoSQL数据库原生提供的原子自增操作,这类操作由数据库底层保证原子性,并发场景下不会出现重复:
- 如果你使用的是Firebase系列数据库,可直接使用
ServerValue.increment(1)完成原子自增 - 如果你使用的是MongoDB,可使用
$inc操作符完成原子增减 - 如果你使用的是Redis,可直接调用
INCR命令完成自增
如果你的NoSQL数据库确实没有提供原生原子自增能力,你需要额外引入分布式锁,在读取ID前先加锁,完成写入操作后再释放锁,避免多个请求同时读写同一个序列值。
内容的提问来源于stack exchange,提问作者Vetrivelu Murugesan
相关产品推荐
相关产品推荐

