文章目录
- 前言
- 一、StackExchange.Redis执行Lua脚本实现商品秒杀
- 1.StackExchange.Redis封装
- 2.秒杀代码
- 3.效果
前言
下面是Redis分布式锁常用的概念说明:设置、获取、过期时间、删除。
1、 Setnx
- 命令:SETNX key value
- 说明:将 key 的值设为 value ,当且仅当 key 不存在。若给定的 key 已经存在,则 SETNX 不做任何动作。SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。 时间复杂度:O(1)
- 返回值:设置成功,返回1 ; 设置失败,返回 0
2、Getset
- 命令:GETSET key value
- 说明:将给定 key 的值设为 value ,并返回 key 的旧值(old value)。当 key 存在但不是字符串类型时,返回一个错误。 时间复杂度:O(1)
- 返回值:返回给定 key 的旧值; 当 key 没有旧值时,也即是, key 不存在时,返回 nil 。
3、Expire
- 命令:EXPIRE key seconds
- 说明:为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。 时间复杂度:O(1)
- 返回值:设置成功返回 1 ;当 key 不存在或者不能为 key 设置生存时间时(比如在低于 2.1.3 版本的 Redis 中你尝试更新 key 的生存时间),返回 0 。
4、Del
- 命令:DEL key [key …]
- 说明:删除给定的一个或多个 key 。不存在的 key 会被忽略。 时间复杂度:O(N); N 为被删除的 key 的数量。删除单个字符串类型的 key ,时间复杂度为O(1)。 删除单个列表、集合、有序集合或哈希表类型的 key ,时间复杂度为O(M), M为以上数据结构内的元素数量。
锁的分类说明:
相对方 | 相对方 |
---|---|
悲观锁 | 乐观锁 |
公平锁 | 非公平锁 |
独享锁 | 共享锁 |
线程锁 | 进程锁 |
一、StackExchange.Redis执行Lua脚本实现商品秒杀
以下以.NET 7控制台为实例测试
1.StackExchange.Redis封装
代码语言:javascript复制using StackExchange.Redis;
namespace Redis
{
public class RedisHelper
{
#region Fileds
private static string? _redisConnection= "127.0.0.1:6379,defaultDatabase=1";
private static int _db = 0;
private static ConnectionMultiplexer? connection;
#endregion
#region Constructors
public RedisHelper()
{
}
public static ConnectionMultiplexer CacheConnection
{
get
{
try
{
if (connection == null || !connection.IsConnected)
{
connection = new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(_redisConnection!)).Value;
}
}
catch (Exception ex)
{
return null;
}
return connection;
}
}
#endregion
#region Methons
/// <summary>
/// 缓存当前数据库
/// </summary>
public static IDatabase CacheRedis => CacheConnection.GetDatabase(_db);
/// <summary>
/// 新增单条值
/// </summary>
/// <param name="values"></param>
/// <returns></returns>
public static bool StringSet(string key, string values)
{
if (string.IsNullOrEmpty(key) && string.IsNullOrEmpty(values))
throw new AggregateException("values or is null");
return CacheRedis.StringSet(key, values);
}
/// <summary>
/// 查询单个key值
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static RedisValue GetStringKey(string key)
{
return CacheRedis.StringGet(key);
}
/// <summary>
/// 判断key是否存储
/// </summary>
/// <param name="key">redis key</param>
/// <returns></returns>
public bool KeyExists(string key)
{
return CacheRedis.KeyExists(key);
}
/// <summary>
/// 删除单个key
/// </summary>
/// <param name="key">redis key</param>
/// <returns>是否删除成功</returns>
public bool KeyDelete(string key)
{
return CacheRedis.KeyDelete(key);
}
/// <summary>
/// redis加锁
/// </summary>
/// <param name="key">需要加锁的锁名</param>
/// <param name="values">需要加锁的值</param>
/// <param name="expireTimeSeconds">该锁自动到期时间,如果没其他要求可设置为最大时常,该方式一定要手动解锁</param>
/// <exception cref="Exception"></exception>
#region 分布式锁
public static bool LockByRedis(string key, string values)
{
try
{
//expireTimeSeconds = expireTimeSeconds > 20 ? 10 : expireTimeSeconds;
//var data = TimeSpan.FromSeconds(expireTimeSeconds);
//var token = Environment.MachineName;
//bool lockflag = CacheRedis.LockTake(key, Thread.CurrentThread.ManagedThreadId, TimeSpan.FromSeconds(expireTimeSeconds));
bool lockflag = CacheRedis.LockTake(key, values, TimeSpan.MaxValue);
if (!lockflag)
{
return false;
}
return true;
}
catch (Exception ex)
{
throw new Exception($"Redis加锁异常:原因{ex.Message}");
}
}
/// <summary>
/// 解锁
/// </summary>
/// <param name="key">需要解锁的锁名</param>
/// <param name="values">需要解锁的值</param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static bool UnLockByRedis(string key, string valuse)
{
try
{
// Thread.CurrentThread.ManagedThreadId
//Environment.MachineName
return CacheRedis.LockRelease(key, valuse);
}
catch (Exception ex)
{
throw new Exception($"Redis加锁异常:原因{ex.Message}");
}
}
#endregion
#endregion
}
}
2.秒杀代码
代码语言:javascript复制using Redis;
int count=11;
Console.WriteLine($"进入秒杀时刻,进入时间{DateTime.Now}");
Parallel.For(0, 1000, i =>
{
//锁键-值
var lockKey = "lockKey";
var lockValue = Guid.NewGuid().ToString("N");
//加锁
bool result = RedisHelper.LockByRedis(lockKey, lockValue);
if (!result)
{
Console.WriteLine("没有抢到锁,这次请求停止");
return;
}
string data = RedisHelper.GetStringKey("Sum").ToString();
int sum = int.Parse(string.IsNullOrEmpty(data) ? "0" : data);
if (sum <= 0)
{
RedisHelper.StringSet("Sum", count.ToString());
}
else
{
if (sum <=count)
{
count--;
Console.WriteLine("商品还剩:" sum.ToString());
RedisHelper.StringSet("Sum", count.ToString());
}
}
//释放锁
var unlock = RedisHelper.UnLockByRedis(lockKey, lockValue);
});
Console.WriteLine($"结束秒杀时刻,结束时间{DateTime.Now}");