基于 Redis 的分布式锁(解决分布式商品超卖问题)

好的,对于小粒度资源锁,可以使用一些更细粒度的锁,例如基于 Redis 的分布式锁。

具体实现方法如下:

使用 Redis 的 setnx 命令尝试获取锁,如果成功则表示获取到了锁,如果失败则表示锁被占用。
在业务逻辑中执行需要加锁的操作,例如扣库存。
使用 Redis 的 del 命令释放锁。

const redis = require('redis');
const client = redis.createClient();

function lock(resource) {
  return new Promise((resolve, reject) => {
    const key = `lock:${resource}`;
    client.setnx(key, 'locked', (err, result) => {
      if (err) {
        reject(err);
      } else if (result === 1) {
        resolve();
      } else {
        reject(new Error('Resource is locked.'));
      }
    });
  });
}

function unlock(resource) {
  const key = `lock:${resource}`;
  client.del(key);
}

// 扣库存的例子
async function buyProduct(productId, quantity) {
  try {
    await lock(`product:${productId}`);
    const stock = await getStock(productId);
    if (stock >= quantity) {
      await updateStock(productId, stock - quantity);
      await updateSales(productId, quantity);
    } else {
      throw new Error('Not enough stock.');
    }
  } finally {
    unlock(`product:${productId}`);
  }
}

async function getStock(productId) {
  // TODO: 查询数据库获取库存
  // ...
  return 100;
}

async function updateStock(productId, newStock) {
  // TODO: 更新数据库的库存
  // ...
}

async function updateSales(productId, quantity) {
  // TODO: 更新数据库的销量
  // ...
}

buyProduct(123, 2)
  .then(() => console.log('购买成功'))
  .catch(err => console.error(err));

在上面的代码中,我们使用 Redis 的 setnx 命令获取锁,使用 del 命令释放锁。在 buyProduct 函数中,我们使用 lock 和 unlock 函数分别获取和释放商品库存的锁。如果锁已经被占用,则 buyProduct 函数抛出一个异常。否则,它会执行数据库操作来扣减库存和增加销量。最后,无论是否发生异常,都会释放锁,以确保其他进程能够访问该资源。