前言
基于Redis的分布式锁实现,原理很简单嘛:检测一下Key是否存在,不存在则Set Key,加锁成功,存在则加锁失败。对吗?这么简单吗?
如果你真这么想,那么你真的需要好好听我讲一下了。接下来,咱们找个例子研究一下。
在开始之前,咱们先定些规则:
- 关于示例代码:
- 需要搭配我准备的示例代码,该示例采用C#编写
- 示例中的材料Id固定为10000
- 示例中的材料初始库存均为100
- 关于Redis中的Key:
- 指示材料库存的Key为
ProductStock_10000
- 自己实现的分布式锁中,指示锁的Key为
DistributedLock_10000
- RedLock.net中,指示锁的Key为
redlock:10000
- 指示材料库存的Key为
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