You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter+Firebase Firestore并发更新字段冲突及锁机制需求

解决Firestore并发更新竞态问题

问题场景与现象

业务需求:更新Firestore集合字段前需检查字段是否为空

  • 字段为空:更新字段并提示成功
  • 字段不为空:仅提示更新失败

并发时出现异常:两名用户几乎同时操作时,两人都收到成功提示,但最终只有后操作的用户数据生效,前用户的更新被覆盖。

问题根源

原有代码采用「先读取字段状态 → 再判断更新」的非原子操作,两步之间存在时间窗口:

  1. 用户1读取字段为空,开始执行更新
  2. 用户2在用户1更新完成前,同样读取到字段为空,也执行更新
  3. 用户2的更新覆盖了用户1的结果

解决方案

Firestore提供两种原子性操作方案,确保「检查+更新」的操作不可拆分,彻底解决竞态问题:

方案1:使用Firestore事务(Transaction)

事务会将「读取文档 → 判断条件 → 更新文档」整个流程封装为原子操作,若事务执行期间文档被其他用户修改,Firestore会自动重试事务,直到成功或超时。

修改后的assignBooking函数:

Future<bool> assignBooking({
  required String bid,
  required String status,
  required String ownerId,
  required String equipmentid,
  required String model,
  required String bookingType,
}) async {
  try {
    return await FirebaseFirestore.instance.runTransaction<bool>((transaction) async {
      final bookingDoc = allBookings.doc(bid);
      final bookingData = await transaction.get(bookingDoc);

      // 检查字段是否为空
      if (bookingData['assigned_equipment_owner_id'] == null || 
          bookingData['assigned_equipment_owner_id'].isEmpty) {
        // 事务内执行更新
        transaction.update(bookingDoc, {
          "status": status,
          'assigned_equipment_owner_id': ownerId,
          'equipment_id': equipmentid,
          'model': model,
          'booking_type': bookingType,
        });
        return true;
      } else {
        return false;
      }
    });
  } catch (e) {
    // 事务失败(如超时、权限问题),返回更新失败
    return false;
  }
}

方案2:使用条件更新(原子写入)

直接在更新操作中添加前置条件,仅当字段为空时才执行更新,整个更新操作是原子性的,无需单独读取文档。

修改后的assignBooking函数:

Future<bool> assignBooking({
  required String bid,
  required String status,
  required String ownerId,
  required String equipmentid,
  required String model,
  required String bookingType,
}) async {
  try {
    await allBookings.doc(bid).update(
      {
        "status": status,
        'assigned_equipment_owner_id': ownerId,
        'equipment_id': equipmentid,
        'model': model,
        'booking_type': bookingType,
      },
      SetOptions(
        // 添加条件:字段为空或不存在时才更新
        condition: Filter.or(
          Filter.isNull('assigned_equipment_owner_id'),
          Filter.equalTo('assigned_equipment_owner_id', ''),
        ),
      ),
    );
    return true;
  } on FirebaseException catch (e) {
    // 条件不满足时抛出failed-precondition异常,返回更新失败
    if (e.code == 'failed-precondition') {
      return false;
    }
    // 其他异常重新抛出
    rethrow;
  } catch (e) {
    return false;
  }
}

注意事项

  • 方案1的事务适合需要基于当前文档状态做复杂判断的场景,Firestore会自动重试冲突的事务
  • 方案2的条件更新更轻量,适合简单的前置条件判断,无需额外读取文档
  • 两种方案都能确保操作的原子性,彻底避免并发覆盖问题

内容的提问来源于stack exchange,提问作者Sindu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 01:52:32