Mongoose 的 save() 方法为什么会导致数据重复插入?如何解决?

介绍

Mongoose 是一个优秀的基于 Node.js 平台的 MongoDB 数据库 ODM(对象-文档映射)工具,开发者可以使用它来快速地将 JavaScript 对象转换为 MongoDB 数据库中的文档,并操作它们进行各类增删改查等操作。

在使用 Mongoose 时,我们通常使用其提供的 save() 方法来将数据保存到 MongoDB 数据库中。然而,有时候你会发现在使用 save() 方法时,可能会出现数据被重复插入的情况。那么,这是由什么原因导致的?该如何解决呢?下面我将为大家详细解释。

原因

在 Mongoose 中,当我们使用 save() 方法保存数据时,实际上是通过执行 insert() 函数来将数据插入到 MongoDB 数据库中。而如果我们使用 save() 方法时调用了多次,而且在两次调用之间 MongoDB 还没有时间从磁盘中加载数据,那么就会导致在数据库中插入两条相同的数据。

原因其实很简单,就是因为 save() 方法并没有考虑到并发请求的情况,而且默认情况下在执行完 save() 方法之前,并没有加锁来保证数据的一致性。因此,如果多个请求同时调用了 save() 方法,就可能会导致数据的重复插入。

解决方法

解决这个问题有很多方法,这里提供两种比较常用的方法:

Solution 1:使用 findOneAndUpdate() 方法

使用 findOneAndUpdate() 方法来保存数据,它的作用是:

  1. 查询指定的文档。
  2. 如果找到,则更新它。
  3. 如果找不到,则插入一个新的文档。

这样,即使在多个请求同时调用时,也只会向数据库中插入一条数据。

这里提供一个示例代码:

const user = await User.findOneAndUpdate(
  { name: 'foo' },
  { name: 'foo', age: 18 },
  { upsert: true, new: true }
);

Solution 2:加锁

在执行 save() 方法之前加锁,确保在写入数据库的数据上没有并发问题。具体可以使用 redis 等缓存系统来实现。在使用缓存系统时,可以在写入 MongoDB 数据库前将数据写入到缓存系统中,获取到锁的请求才能进入下一步写入 MongoDB 数据库的过程,避免了多个请求同时写入 MongoDB 数据库的问题。

这里提供一个示例代码:

const redlock = require('redlock');
const lock = await redlock.lock('my-lock-key');
await User.save();
await lock.unlock();

总结

在并发情况下,使用 Mongoose 的 save() 方法可能会出现数据重复插入的情况。为了避免这种情况的发生,我们可以使用 findOneAndUpdate() 方法或加锁等方案来确保数据的一致性。同时,我们也应该在编写 Node.js 应用程序时,更加谨慎地处理异步 I/O 和并发用户的请求,保证应用程序的稳定性和可靠性。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65b1e052add4f0e0ffb12df9