
网上查了一些,但那些文章写的云里雾里(可能是我搜索的关键字不对吧?)
想问问各位大佬是如何做的 ( 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} ] 1 xudong 2022 年 11 月 15 日 建议用 redis ,然后定期(每秒)把 redis 内的值写入到 db 。 |
2 undownding 2022 年 11 月 15 日 db.update(id, { $inc: { pv: 1} }) |
3 undownding 2022 年 11 月 15 日 ``` function counter(id){ db.updateOne(id, { $inc: { pv: 1}, $setOnInsert: { pv: 1 } }, { upsert: true }) } ``` |
4 swulling 2022 年 11 月 15 日 via iPhone 先读后写肯定并发不安全啊。 要么使用 incr 原子操作。要么加锁。 |
5 lete OP @undownding 谢谢,有用,好奇如果在不知道这些操作符的情况下要怎么搜索,总不能看着官方文档,一个一个翻吧? |
6 leopod1995 2022 年 11 月 15 日 $inc 或者 findOneAndUpdate() 本身就是原子操作可以保证幂等性。 技术角度最简单的就是一个请求一次 db , 从业务执行来讲,1L 方案是比较正确的。 |
7 yimity 2022 年 11 月 15 日 这个问题跟 nodejs 并没有关系。 其他人说的原子操作。 做事情之前应该的步骤是大概翻一下官方文档知道都有什么,可以用什么吧。 |
8 shiyanfei5 2022 年 11 月 15 日 mongoDb 没事务吗? |
9 shiyanfei5 2022 年 11 月 15 日 @shiyanfei5 说错,如果用数据库做的话,可以看下有没有类似 for update 之类的功能可以。。。 |