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

如何处理NodeJS Express接口请求的竞态条件问题

问题判断

你的判断完全正确,这段代码确实存在严重的竞态条件问题。

问题原因

Express默认会并行处理所有入站请求,当两个请求几乎同时到达时:

  1. 请求A先执行await databaseGetFoo(),拿到的foo值为true
  2. 此时请求A还没来得及执行databaseSetFoo把foo改成false,请求B也执行了await databaseGetFoo(),拿到的foo同样是true
  3. 两个请求都会进入if判断分支,somethingThatShouldOnlyBeDoneOnce()会被执行两次,完全不符合预期。

解决方案

下面是几种常用的规避方案,按推荐优先级排序:

  • 优先使用数据库原子操作(最稳妥)
    把「查询foo值 + 修改foo值」的两步操作合并成数据库层面的单步原子操作,不要分开在应用层做判断。示例逻辑如下:
    app.get('/', async (req, res) => {
      // 直接执行带条件的更新,数据库会自带事务保证原子性
      const updateResult = await databaseUpdateFooIfMatch({oldVal: true, newVal: false});
      // 只有更新影响行数为1时,说明当前请求是第一个拿到执行权的
      if (updateResult.affectedRows === 1) {
        somethingThatShouldOnlyBeDoneOnce();
      }
    })
    
    这种方案不需要额外引入中间件,靠数据库本身的事务能力就能解决问题,兼容性最好,单机、分布式部署都适用。
  • 加乐观锁
    给foo对应的数据库行加一个版本号字段,每次查询的时候拿到当前版本号,更新的时候携带版本号作为条件:UPDATE 表 SET foo = false, version = version + 1 WHERE id = 对应ID AND version = 查出来的版本号,同样通过影响行数判断是否更新成功,适合更复杂的判断场景。
  • 加分布式锁
    如果你的逻辑不能放到数据库层处理,可以引入Redis等分布式锁组件,处理逻辑前先尝试抢锁,只有抢到锁的请求才能执行后续逻辑,处理完成后释放锁。如果是单实例部署的Express,也可以用简单的内存锁替代,但分布式部署场景下内存锁不生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 04:36:01