V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
lete
V2EX  ›  程序员

关于 nodejs 对数据库操作产生重复数据的问题(幂等性)

  •  
  •   lete ·
    Lete114 · 2022-11-15 12:16:05 +08:00 · 1555 次点击
    这是一个创建于 739 天前的主题,其中的信息可能已经有所发展或是发生改变。

    网上查了一些,但那些文章写的云里雾里(可能是我搜索的关键字不对吧?)

    想问问各位大佬是如何做的 ( nodejs+mongodb )

    功能计数器: 每有一个请求访问网站就会+1 ,并保存到数据库中(每个页面有自己的计数器,也就是 pv 统计)

    伪代码:

    // 假设请求服务器的时候 nodejs 会调用这个方法
    // id 为页面的唯一表示
    function counter(id){
      // 如果数据库中没有数据则插入、否则修改并+1
      const result = db.select(id)
      if(result){
        result.pv++
        db.update(id, { pv: result.pv })
        return result
      }
      db.insert(id, { pv: 1 })
      return { pv:1 }
    }
    

    问题 1: 假设一开始数据库中并无某个页面数据,这时突然来了两个(以上)请求(id 相同),那么当调用 counter() 时会先查询数据库中是否有数据,此时第一个请求开始 select 发现数据库中并无数据,但还没来得及 insert 第二个请求就开始了 select ,此时第二个请求也发现数据库并无数据,最后就会执行两个插入操作导致数据重复

    实际结果:

    [
      {_id: 1, id: "A", pv: 1}, 
      {_id: 2, id: "A", pv: 1}
    ]
    

    期望结果:

    [
      {_id: 1, id: "A", pv: 2}
    ]
    

    问题 2: 假设数据库中已有一条数据 [{_id: 1, id: "A", pv: 1}],突然来了 3 个(以上)请求,3 个请求都会去查询数据库,得到的结果全部相同 pv 都是 1 ,这 3 个请求都会执行 update

    实际结果:

    [
      {_id: 1, id: "A", pv: 2}
    ]
    

    期望结果:

    [
      {_id: 1, id: "A", pv: 4}
    ]
    
    9 条回复    2022-11-15 22:29:20 +08:00
    xudong
        1
    xudong  
       2022-11-15 12:22:19 +08:00
    建议用 redis ,然后定期(每秒)把 redis 内的值写入到 db 。
    undownding
        2
    undownding  
       2022-11-15 12:26:22 +08:00
    db.update(id, { $inc: { pv: 1} })
    undownding
        3
    undownding  
       2022-11-15 12:29:51 +08:00
    ```
    function counter(id){
    db.updateOne(id, { $inc: { pv: 1}, $setOnInsert: { pv: 1 } }, { upsert: true })
    }
    ```
    swulling
        4
    swulling  
       2022-11-15 12:31:31 +08:00 via iPhone
    先读后写肯定并发不安全啊。

    要么使用 incr 原子操作。要么加锁。
    lete
        5
    lete  
    OP
       2022-11-15 13:46:34 +08:00
    @undownding 谢谢,有用,好奇如果在不知道这些操作符的情况下要怎么搜索,总不能看着官方文档,一个一个翻吧?
    leopod1995
        6
    leopod1995  
       2022-11-15 14:36:46 +08:00
    $inc 或者 findOneAndUpdate() 本身就是原子操作可以保证幂等性。

    技术角度最简单的就是一个请求一次 db , 从业务执行来讲,1L 方案是比较正确的。
    yimity
        7
    yimity  
       2022-11-15 16:49:15 +08:00
    这个问题跟 nodejs 并没有关系。
    其他人说的原子操作。
    做事情之前应该的步骤是大概翻一下官方文档知道都有什么,可以用什么吧。
    shiyanfei5
        8
    shiyanfei5  
       2022-11-15 22:18:12 +08:00
    mongoDb 没事务吗?
    shiyanfei5
        9
    shiyanfei5  
       2022-11-15 22:29:20 +08:00
    @shiyanfei5 说错,如果用数据库做的话,可以看下有没有类似 for update 之类的功能可以。。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1913 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 16:19 · PVG 00:19 · LAX 08:19 · JFK 11:19
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.