基于Redis的分布式锁设计

前言

基于Redis的分布式锁实现,原理很简单嘛:检测一下Key是否存在,不存在则Set Key,加锁成功,存在则加锁失败。对吗?这么简单吗?

如果你真这么想,那么你真的需要好好听我讲一下了。接下来,咱们找个例子研究一下。

在开始之前,咱们先定些规则:

  • 关于示例代码:
    • 需要搭配我准备的示例代码,该示例采用C#编写
    • 示例中的材料Id固定为10000
    • 示例中的材料初始库存均为100
  • 关于Redis中的Key:
    • 指示材料库存的Key为ProductStock_10000
    • 自己实现的分布式锁中,指示锁的Key为DistributedLock_10000
    • RedLock.net中,指示锁的Key为redlock:10000

1、假如没有锁

如果没有锁,我们可以通过Jmeter并发100个请求,看看最后库存是不是0

/// <summary>
/// 无锁扣减库存
/// </summary>
/// <returns></returns>
[HttpPost("DecreaseProductStockWithNoLock")]
public async Task<string> DecreaseProductStockWithNoLock()
{
    var stockKey = GetProductStockKey(ProductId);
    var currentQuantity = (long)(await _redisDatabase.Database.StringGetAsync(stockKey));
    if (currentQuantity < 1)
        throw new Exception("库存不足");

    var leftQuantity = currentQuantity - 1;
    await _redisDatabase.Database.StringSetAsync(stockKey, leftQuantity);

    return $"剩余库存:{leftQuantity}";
}

完了,库存全乱了,收拾收拾,跑路吧o(╥﹏╥)o!

2、单应用中的锁

提到锁,大多数人首先想到的应该就是Monitor的语法糖lock了,这是大多数人最先接触到的一种锁。在单应用中,因为lock是线程锁,所以使用该锁一般是没有什么问题的。

/// <summary>
/// 在单应用中扣减库存
/// </summary>
/// <returns></returns>
[HttpPost("DecreaseProductStockInSingleApp")]
public string DecreaseProductStockInSingleApp()
{
    long leftQuantity;
    lock (_lockObj)
    {
        var stockKey = GetProductStockKey(ProductId);
        var currentQuantity = (long)_redisDatabase.Database.StringGet(stockKey);
        if (currentQuantity < 1)
            throw new Exception("库存不足");

        leftQuantity = currentQuantity - 1;
        _redisDatabase.Database.StringSet(stockKey, leftQuantity);
    }

    return $"剩余库存:{leftQuantity}";
}

结果和我们所期望的一样,剩余库存为0

但是如果我们进行应用集群,部署多份一模一样的应用,那lock就无能为力了。接下来,咱们启动两个应用实例来看看

# 以开发环境运行,能看到更多信息
dotnet XXTk.Redis.DistributedLock.Api.dll --urls http://localhost:5000 --environment Development

dotnet XXTk.Redis.DistributedLock.Api.dll --urls http://localhost:5010 --environment Development

可见,一共发送了100个请求,本应该最后库存为0的,却还剩17个

3、应用集群中的锁

3.1版本1

很明显,lock已经没用了,是时候进入咱们的主题了——基于Redis的分布式锁设计。

初步的思路是这样的:

  1. 将材料Id作为Redis Key
  2. 如果Redis中存在该Key,则认为锁已经被其他线程占用了
  3. 如果Redis中不存在Key,则将该Key添加到Redis中,Value则随意赋值
  4. 当获取到锁的业务执行完毕后,将Key从Redis中移除

有了思路,接下来就该想一下如何实现了。很幸运,Redis的命令SETNX key value完全满足我们的需求,实现如下:

/// 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值