基于 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 函数抛出一个异常。否则,它会执行数据库操作来扣减库存和增加销量。最后,无论是否发生异常,都会释放锁,以确保其他进程能够访问该资源。