Redis分布式锁防止缓存击穿

一、Nuget引入 StackExchange.RedisDistributedLock.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));

 

 

posted @ 2024-08-05 16:12  DaiWK  阅读(12)  评论(0编辑  收藏  举报