Nodejs项目里面遇到了并发的问题。怎么增加数据库锁的机制

Nodejs项目里面遇到了并发的问题。怎么增加数据库锁的机制

使用了 express + mongoose的 架构 在实现一个逻辑的时候

第一步 从数据库里面 查询一条记录 然后插入改变另一个表的某个属性值 +1

结果当并发操作的时候就出现了 重复改变另一张表的情况 这个有什么办法能解决吗?

3 回复

当然可以!在处理并发问题时,特别是在涉及到多个用户同时对同一数据进行操作的情况下,使用数据库锁是一种常见的解决方案。这里我们将讨论如何在 Node.js 和 Mongoose 中实现这一点。

解决方案:使用悲观锁(数据库锁)

悲观锁意味着在查询数据时就锁定该记录,直到事务完成。这样可以确保在更新过程中没有其他请求能够修改这条记录。

示例代码

假设我们有两个集合(collection):userspoints。我们需要在更新用户信息时,确保不会发生并发问题。

const mongoose = require('mongoose');
const User = mongoose.model('User', new mongoose.Schema({ name: String, points: Number }));
const Point = mongoose.model('Point', new mongoose.Schema({ user_id: String, points: Number }));

async function updateUserPoints(userId) {
    // 开始事务
    const session = await mongoose.startSession();
    session.startTransaction();

    try {
        // 使用 findAndModify 方法获取并锁定记录
        const user = await User.findOneAndUpdate(
            { _id: userId },
            { $inc: { points: 1 } }, 
            { session, new: true, upsert: false, fields: { __v: 0 } }
        );

        // 更新点数表
        const point = await Point.findOneAndUpdate(
            { user_id: userId },
            { $inc: { points: 1 } }, 
            { session, new: true, upsert: true }
        );

        // 提交事务
        await session.commitTransaction();
        session.endSession();
        
        console.log(`Updated points for user ${userId}`);
    } catch (error) {
        // 如果有错误,回滚事务
        await session.abortTransaction();
        session.endSession();
        throw error;
    }
}

// 调用函数
updateUserPoints('someUserId')
    .then(() => console.log('Success'))
    .catch(err => console.error('Error:', err));

解释

  1. 事务管理:我们使用 mongoose.startSession() 创建一个新的会话,并通过 session.startTransaction() 开始一个事务。这确保了所有操作要么全部成功,要么全部失败。

  2. 查找并锁定记录:我们使用 findOneAndUpdate 方法来查找并更新用户记录。通过传递 { session: session } 参数,我们告诉 Mongoose 在事务中执行这个操作,从而实现了悲观锁的效果。

  3. 提交或回滚事务:如果所有操作都成功,则调用 session.commitTransaction() 来提交事务。如果任何操作失败,我们调用 session.abortTransaction() 来回滚事务。

通过这种方式,我们可以有效地防止并发问题导致的数据不一致。


使用队列来暂存所需要的独占操作。当需要执行这种操作时,在队列里添加要操作的数据以及回调函数就行了。另用一段代码来消费这个队列。

在Node.js项目中使用Express和Mongoose架构时,处理并发问题可以通过数据库级别的锁机制来解决。Mongoose本身没有内置的锁机制,但你可以通过MongoDB的特性来实现这一功能。

以下是一个简单的示例,展示如何在更新数据时使用MongoDB的findAndModify方法,这可以在一定程度上模拟乐观锁的行为:

const updateRecord = async (id, incrementValue) => {
    const session = await mongoose.startSession();
    session.startTransaction();

    try {
        // 查询记录
        const record = await MyModel.findOne({ _id: id }).session(session);

        if (!record) {
            throw new Error('Record not found');
        }

        // 更新记录并增加值
        await MyModel.updateOne(
            { _id: id },
            { $inc: { targetField: incrementValue } },
            { session }
        );

        // 提交事务
        await session.commitTransaction();
        session.endSession();
    } catch (error) {
        // 回滚事务
        await session.abortTransaction();
        session.endSession();
        throw error;
    }
};

在这个例子中,我们使用了MongoDB的会话(session)和事务(transaction)功能。这样可以确保在并发情况下只有一次操作能够成功提交,从而避免了重复修改的问题。

解释

  • 会话(Session):创建一个新的会话,并在其中启动一个事务。
  • 查找记录:查询需要更新的记录。
  • 更新记录:通过$inc操作符对指定字段进行加法运算。
  • 提交事务:如果所有步骤都正确执行,则提交事务。如果有任何错误发生,则回滚事务。

这种方法可以有效避免并发访问导致的数据不一致问题。请注意,为了使用事务,你的MongoDB集群需要支持多文档事务。对于某些版本的MongoDB,这可能需要副本集或分片集群的支持。

回到顶部