Firestore调用FieldValue.increment自增后快速读取数值异常
问题根因
出现重复、跳号不是FieldValue.increment本身的计数bug,是并发场景下独立读写请求的时序不匹配导致的:
FieldValue.increment是Firestore提供的原子操作,不管并发多高,服务端最终执行的累加结果一定和总调用次数一致,数据库里存储的t值不会错。- 你当前代码里的
update累加、get读值是两个完全独立的网络请求,快速连续触发函数时会出现请求排队错位:- 前一次调用的
update还没在服务端落盘,后一次调用的update已经发出,多个累加操作先后在服务端执行完成,但后续跟进的多个get请求可能命中同一个版本的文档快照,就会打印重复值。 - 如果某个
get请求因网络延迟跳过了中间版本的文档快照,就会出现跳号。
- 前一次调用的
- 放慢点击速度时,前一次调用的「update落盘 + get拉取最新值」全流程已经走完,下一次调用才发起请求,自然不会出现时序错乱,准确率就会提升。
解决方案
不要在update之后单独发get请求拉取值,直接利用Firestore的实时快照能力同步服务端最新状态,从API层面规避并发时序问题,这是性能和稳定性最优的写法:
// 页面/组件全局初始化一次文档引用即可 final ref = FirebaseFirestore.instance.collection('test').doc('t'); // 点击回调只做原子累加,不单独发起读请求 Future<void> _custom() async { await ref.update({'t': FieldValue.increment(1)}); } // 页面初始化时就注册文档快照监听,服务端值变更时自动同步 @override void initState() { super.initState(); ref.snapshots().listen((docSnapshot) { if (docSnapshot.exists) { final currentT = docSnapshot.data()!['t'] as int; print(currentT); // 此处同步更新页面展示的数值即可 } }); }
如果不想用长连接监听快照,也可以用事务保证读写强一致,适合对一致性要求极高但调用频率不高的场景,性能比原子累加+快照监听稍差:
Future<void> _custom() async { await FirebaseFirestore.instance.runTransaction((transaction) async { final docSnap = await transaction.get(ref); final currentValue = docSnap.data()!['t'] as int; final newValue = currentValue + 1; transaction.update(ref, {'t': newValue}); // 事务内拿到的一定是执行完累加的最新值,不会出现时序错位 print(newValue); }); }
不要试图靠客户端加防抖、点击锁来规避这个问题,网络波动时哪怕单次点击也可能出现读写时序错位,从Firestore API特性层面适配才是稳定解法。
内容的提问来源于stack exchange,提问作者Subair K
相关产品推荐
相关产品推荐

