下面是Redis分布式锁常用的概念说明:设置、获取、过期时间、删除。
1、 Setnx
2、Getset
3、Expire
4、Del
锁的分类说明:
| 相对方 | 相对方 |
|---|---|
| 悲观锁 | 乐观锁 |
| 公平锁 | 非公平锁 |
| 独享锁 | 共享锁 |
| 线程锁 | 进程锁 |
以下以.NET 7控制台为实例测试
using CSRedis;
Console.WriteLine("Hello World!");
//连接redis客户端
CSRedisClient redisClient = new CSRedisClient("127.0.0.1:6379,defaultDatabase=1");
//锁键
var lockKey = "lockKey";
//库存数
var stockKey = "stock";
//设置库存数量为5
redisClient.Set(stockKey, 5);//商品库存
//lua脚本,也可以通过流形式加载进来
var releaseLockScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";//释放锁的redis脚本
redisClient.Del(lockKey);//测试前,先把锁删了.
//------------------Parallel可以控制线程的执行顺序-----------------------------------
//ParallelOptions parallelOptions = new ParallelOptions();
//parallelOptions.MaxDegreeOfParallelism = 2;//最大并行数
//Parallel.For(0, 10, parallelOptions, t => this.DoSomethingLong($"btnParallel_Click_{t}"));
Parallel.For(0, 10, i =>
{
var id = Guid.NewGuid().ToString("N");
//获取锁,有效期5秒,防止死锁产生
do
{
//set : key存在则失败,不存在才会成功,并且过期时间5秒
var success = redisClient.Set(lockKey, id, expireSeconds: 5, exists: RedisExistence.Nx);
if (success == true)
{
break;
}
Thread.Sleep(TimeSpan.FromSeconds(1));//休息1秒再尝试获取锁
} while (true);
Console.WriteLine($"线程:{Task.CurrentId} 拿到了锁,开始消费");
//扣减库存
var currentStock = redisClient.IncrBy(stockKey, -1);
if (currentStock < 0)
{
Console.WriteLine($"库存不足,线程:{Task.CurrentId} 抢购失败!");
redisClient.Eval(releaseLockScript, lockKey, id);
return;
}
//模拟处理业务,这里不考虑失败的情况
Thread.Sleep(TimeSpan.FromSeconds(new Random().Next(1, 3)));
Console.WriteLine($"线程:{Task.CurrentId} 消费完毕!剩余 {currentStock} 个");
//业务处理完后,释放锁.
redisClient.Eval(releaseLockScript, lockKey, id);
});


运行两个进程测试秒杀情况
using CSRedis;
Thread.Sleep(500);
Console.WriteLine("Hello World!");
CSRedisClient redisClient = new CSRedisClient("127.0.0.1:6379,defaultDatabase=1");
var lockKey = "lockKey";
//库存数
var stockKey = "stock";
//设置库存数量为5
redisClient.Set(stockKey, 10000, exists: RedisExistence.Nx);//商品库存
for (int i = 0; i < 100; i++)
{
Thread.Sleep(500);
var releaseLockScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";//释放锁的redis脚本
var id = Guid.NewGuid().ToString("N");
//set : key存在则失败,不存在才会成功,并且不过期
var success = redisClient.Set(lockKey, id, expireSeconds: -1, exists: RedisExistence.Nx);
if (success != true)
{
Console.WriteLine("当前资源以被占用,没机会了,等10秒钟再次尝试");
Thread.Sleep(1000);//休息1秒再尝试获取锁
continue;
}
else
{
Console.WriteLine($"线程:{Task.CurrentId} 拿到了锁,开始消费");
}
///业务操作
var currentStock = redisClient.IncrBy(stockKey, -1);
if (currentStock < 0)
{
Console.WriteLine($"库存不足,线程:{Task.CurrentId} 抢购失败!");
redisClient.Eval(releaseLockScript, lockKey, id);
return;
}
//模拟处理业务,这里不考虑失败的情况
Thread.Sleep(TimeSpan.FromSeconds(new Random().Next(1, 3)));
Console.WriteLine($"线程:{Task.CurrentId} 消费完毕!剩余 {currentStock} 个");
//业务处理完后,释放锁.
redisClient.Eval(releaseLockScript, lockKey, id);
}
