nodejs多进程操作数据库带锁

2025-04-14 15

Image

nodejs多进程操作数据库带锁

在Node.js中,多进程操作数据库时可能会遇到并发问题,尤其是当多个进程同时修改同一数据时。为了解决这个问题,我们可以引入锁机制来确保数据的一致性。介绍如何通过不同的方法实现带锁的数据库操作。

解决方案

探讨了三种解决方案:使用文件锁、使用数据库自身的锁机制(如MySQL的SELECT ... FOR UPDATE)、以及使用Redis作为分布式锁。每种方法都有其适用场景和优缺点,我们将逐一分析并提供代码示例。


方法一:使用文件锁

文件锁是一种简单的锁机制,适用于单机环境下的多进程操作。我们可以通过fs模块结合flock库来实现文件锁。

实现步骤

  1. 安装flock库。
  2. 在操作数据库前获取文件锁。
  3. 数据库操作完成后释放锁。

示例代码

javascript
const fs = require('fs');
const flock = require('flock');</p>

<p>// 数据库连接池
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'test_db'
});</p>

<p>// 锁文件路径
const lockFilePath = '/tmp/db_lock';</p>

<p>async function withLock(fn) {
    const lockFileDescriptor = fs.openSync(lockFilePath, 'w');
    try {
        // 尝试获取锁
        flock(lockFileDescriptor, 'ex'); // exclusive lock
        return await fn();
    } finally {
        flock(lockFileDescriptor, 'un'); // unlock
        fs.closeSync(lockFileDescriptor);
    }
}</p>

<p>async function updateDatabase() {
    await withLock(async () => {
        const [rows] = await pool.execute('UPDATE users SET points = points + 1 WHERE id = ?', [1]);
        console.log('Database updated successfully.');
    });
}</p>

<p>// 启动多个进程模拟并发
for (let i = 0; i < 5; i++) {
    process.fork().on('exit', () => {
        updateDatabase();
    });
}

优点:简单易用,适合单机环境。
缺点:不支持分布式环境,锁文件可能成为性能瓶颈。


方法二:使用数据库自带的锁机制

许多关系型数据库(如MySQL)提供了事务和行级锁功能,可以利用这些特性来解决并发问题。

实现步骤

  1. 使用SELECT ... FOR UPDATE语句锁定目标行。
  2. 在事务中完成更新操作。
  3. 提交或回滚事务。

示例代码

javascript
const mysql = require('mysql2/promise');</p>

<p>// 数据库连接池
const pool = mysql.createPool({
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'test_db'
});</p>

<p>async function updateWithTransaction() {
    const connection = await pool.getConnection();
    try {
        await connection.beginTransaction();</p>

<pre><code>    // 获取锁
    const [rows] = await connection.execute('SELECT * FROM users WHERE id = ? FOR UPDATE', [1]);
    if (rows.length === 0) {
        throw new Error('User not found');
    }

    // 更新数据
    await connection.execute('UPDATE users SET points = points + 1 WHERE id = ?', [1]);

    await connection.commit();
    console.log('Transaction committed successfully.');
} catch (err) {
    await connection.rollback();
    console.error('Transaction failed:', err.message);
} finally {
    connection.release();
}

}

// 启动多个进程模拟并发
for (let i = 0; i < 5; i++) {
process.fork().on('exit', () => {
updateWithTransaction();
});
}

优点:无需额外依赖,直接利用数据库的锁机制。
缺点:锁粒度较大,可能导致性能下降;不适合非关系型数据库。


方法三:使用Redis作为分布式锁

Redis是一个高性能的内存数据库,支持原子操作,非常适合实现分布式锁。

实现步骤

  1. 使用SETNX命令尝试设置锁。
  2. 如果成功设置锁,则执行数据库操作。
  3. 操作完成后释放锁。

示例代码

javascript
const redis = require('redis');
const client = redis.createClient();</p>

<p>const mysql = require('mysql2/promise');
const pool = mysql.createPool({
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'test_db'
});</p>

<p>const LOCK<em>KEY = 'db</em>update<em>lock';
const LOCK</em>EXPIRE = 5000; // 锁过期时间(毫秒)</p>

<p>function acquireLock(key, expire) {
    return new Promise((resolve, reject) => {
        client.set(key, 'locked', 'NX', 'EX', Math.floor(expire / 1000), (err, result) => {
            if (err) return reject(err);
            resolve(result === 'OK');
        });
    });
}</p>

<p>function releaseLock(key) {
    return new Promise((resolve, reject) => {
        client.del(key, (err, result) => {
            if (err) return reject(err);
            resolve(result > 0);
        });
    });
}</p>

<p>async function updateWithRedisLock() {
    const isLocked = await acquireLock(LOCK<em>KEY, LOCK</em>EXPIRE);
    if (!isLocked) {
        console.error('Failed to acquire lock.');
        return;
    }</p>

<pre><code>try {
    const [rows] = await pool.execute('UPDATE users SET points = points + 1 WHERE id = ?', [1]);
    console.log('Database updated successfully.');
} catch (err) {
    console.error('Database update failed:', err.message);
} finally {
    await releaseLock(LOCK_KEY);
    console.log('Lock released.');
}

}

// 启动多个进程模拟并发
for (let i = 0; i < 5; i++) {
process.fork().on('exit', () => {
updateWithRedisLock();
});
}

优点:支持分布式环境,锁粒度细,性能高。
缺点:需要额外部署Redis服务。


以上三种实现Node.js多进程操作数据库带锁的方法:
1. 文件锁:适合单机环境,简单易用。
2. 数据库自带锁机制:无需额外依赖,但锁粒度较大。
3. Redis分布式锁:支持分布式环境,性能优越。

根据实际需求选择合适的方案。如果只是单机环境,文件锁或数据库锁即可满足需求;如果是分布式系统,则推荐使用Redis锁。

1. 本站所有资源来源于用户上传和网络,因此不包含技术服务请大家谅解!如有侵权请邮件联系客服!cheeksyu@vip.qq.com
2. 本站不保证所提供下载的资源的准确性、安全性和完整性,资源仅供下载学习之用!如有链接无法下载、失效或广告,请联系客服处理!
3. 您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容资源!如用于商业或者非法用途,与本站无关,一切后果请用户自负!
4. 如果您也有好的资源或教程,您可以投稿发布,成功分享后有积分奖励和额外收入!
5.严禁将资源用于任何违法犯罪行为,不得违反国家法律,否则责任自负,一切法律责任与本站无关

源码下载