Redis分布式锁防止缓存击穿
一、Nuget引入 StackExchange.Redis、DistributedLock.Redis依赖
二、使用 StackExchange.Redis 对redis操作做简单封装
public class RedisHelper { private static ConnectionMultiplexer _redis; private static string _connectionString; // 静态构造函数,确保在程序启动时初始化连接 static RedisHelper() { _connectionString = "127.0.0.1:6379,password=ist123$%^"; // 替换为你的Redis服务器地址和端口,例如:"localhost:6379" _redis = ConnectionMultiplexer.Connect(_connectionString); } // 获取数据库实例 public static IDatabase GetDatabase() { return _redis.GetDatabase(); } // 字符串设置与获取 public static void SetString(string key, string value) { GetDatabase().StringSet(key, value); } public static string GetString(string key) { return GetDatabase().StringGet(key); } // 哈希设置与获取 public static void SetHashField(string key, string field, string value) { GetDatabase().HashSet(key, field, value); } public static string GetHashField(string key, string field) { return GetDatabase().HashGet(key, field); } // 列表操作 public static long ListRightPush(string key, string value) { return GetDatabase().ListRightPush(key, value); } public static string ListLeftPop(string key) { return GetDatabase().ListLeftPop(key); } // 集合操作 public static bool SetAdd(string key, string value) { return GetDatabase().SetAdd(key, value); } public static bool SetRemove(string key, string value) { return GetDatabase().SetRemove(key, value); } public static bool SetContains(string key, string value) { return GetDatabase().SetContains(key, value); } // 键的其他操作 public static bool KeyExists(string key) { return GetDatabase().KeyExists(key); } public static void Remove(string key) { GetDatabase().KeyDelete(key); } // 异步方法示例 public static async Task SetStringAsync(string key, string value) { await GetDatabase().StringSetAsync(key, value); } public static async Task<string> GetStringAsync(string key) { return await GetDatabase().StringGetAsync(key); } // 关闭连接(通常在应用程序关闭时调用) public static void CloseConnection() { if (_redis != null && _redis.IsConnected) { _redis.Close(); _redis.Dispose(); } } }
三、模拟从Redis获取缓存数据的逻辑
/// <summary> /// 模拟操作 /// </summary> public class RedisOper { public static string GetDataByRedis(string RedisKey) { string ThreadId = Thread.GetCurrentProcessorId().ToString(); string data = string.Empty; //从redis读数据 if (RedisHelper.KeyExists(RedisKey)) { data = RedisHelper.GetString(RedisKey); Console.WriteLine($"查询到缓存数据:{data} 线程ID:{ThreadId}"); } else { Console.WriteLine($"未查询到缓存数据,准备从数据库查询存入缓存 线程ID:{ThreadId}"); //模拟从数据库查询存入redis,使用分布式锁,只允许一个请求完成 var redisDistributedLock = new RedisDistributedLock("lockkey", RedisHelper.GetDatabase()); Console.WriteLine($"尝试获取锁 线程ID:{ThreadId}"); using (redisDistributedLock.Acquire()) { //再次判断是否存在缓存 if (!RedisHelper.KeyExists(RedisKey)) { Console.WriteLine($"获取到锁,模拟将数据库数据写入缓存 线程ID:{ThreadId}"); var GetDataByDB = "This is test data"; RedisHelper.SetString(RedisKey, GetDataByDB); Thread.Sleep(2000); } } } return data; } public static async Task<string> GetDataByRedisAsync(string RedisKey) { Console.WriteLine($"当前线程ID:{Thread.GetCurrentProcessorId()}"); string data = string.Empty; //从redis读数据 if (RedisHelper.KeyExists(RedisKey)) { data = await RedisHelper.GetStringAsync(RedisKey); Console.WriteLine($"查询到缓存数据:{data}"); } else { Console.WriteLine($"未查询到缓存数据,准备从数据库查询存入缓存"); //模拟从数据库查询存入redis,使用分布式锁,只允许一个请求完成 var redisDistributedLock = new RedisDistributedLock("lockkey", RedisHelper.GetDatabase()); Console.WriteLine($"尝试获取锁 "); using (redisDistributedLock.Acquire()) { //再次判断是否存在缓存 if (!RedisHelper.KeyExists(RedisKey)) { Console.WriteLine("获取到锁,模拟将数据库数据写入缓存"); var GetDataByDB = "This is test data"; await RedisHelper.SetStringAsync(RedisKey, GetDataByDB); await Task.Delay(2000); } } } return data; } }
四、测试
string redisKey = "testData"; RedisHelper.Remove(redisKey); //模拟10个线程同时去获取Redis缓存 List<Task> tasksList= new List<Task>(); tasksList.Add(Task.Run(()=>RedisOper.GetDataByRedis(redisKey))); tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey))); tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey))); tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey))); tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey))); tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey))); tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey))); tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey))); tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey))); tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey))); Task.WaitAll(tasksList.ToArray()); await Task.Run(() => RedisOper.GetDataByRedis(redisKey));
喜欢 C#、SQL、Web