首页 > 数据库 >Redis分布式锁防止缓存击穿

Redis分布式锁防止缓存击穿

时间:2024-08-05 16:18:13浏览次数:12  
标签:GetDatabase 缓存 string Redis Task static key public 分布式

一、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));

 

 

标签:GetDatabase,缓存,string,Redis,Task,static,key,public,分布式
From: https://www.cnblogs.com/daiwk/p/18343446

相关文章

  • 关于Redis的面试
    一、Redis介绍Redis是一个开源的远程字典服务,使用C语言编写、支持网络调用、基于内存亦可持久化的Key-Value数据库,并提供多种语言的API。二、为什么要使用Redis内存数据库,速度很快工作单线程worker,串行化,原子操作,IO线程是多线程的。避免上下文切换使用IO模型,天生支撑......
  • 分布式锁的实现:基于Redis的解决方案
    微服务架构中,分布式锁是确保跨多个节点或实例的线程安全的关键技术。当多个服务实例可能同时修改共享资源时,分布式锁可以保证在同一时间只有一个实例可以执行特定的操作。在本文中,我们将探讨如何使用Redis来实现一个简单的分布式锁机制。分布式锁的挑战在分布式系统中,锁必须满足......
  • 【Redis】全局命令/内部编码/浅谈单线程模型
    目录前言两个核心命令GET和SET全局命令KEYSEXISTS DELEXPIRETTLTYPE 数据结构的内部编码Redis的5中数据类型Redis数据结构和内部编码单线程架构前言Redis提供了5种数据结构,理解每种数据结构的特点对于Redis开发运维⾮常重要,同时掌握每种数据结构的常......
  • redis通过滑动窗口实现限流
    一、什么是滑动窗口限流滑动窗口限流是一种流量控制策略,用于控制在一定时间内允许执行的操作数量或请求频率。它的工作方式类似于一个滑动时间窗口,对每个时间窗口的请求数量进行计数,并根据预先设置的限流策略来限制或调节流量,通常包括以下几个要素:时间窗口:限流的时间段,例如每秒、......
  • redis
    redis安装目录规划:###redis下载目录/data/soft/[###redis安装目录/opt/redis_cluster/redis{PORT}/{conf,logs,pid}###redis数据目录/data/redis_cluster/redis{PORT}/redis{PORT}.rdb###redis运维脚本/root/scripts/redisshell.sh安装命令:编辑hosts......
  • redis+xxl-job初步设计点赞功能
    一般情况下点赞业务涉及以下下几个方面:1.我们肯定要知道一个题目被多少人点过赞,还要知道,每个人他点赞了哪些题目。2.点赞的业务特性,频繁。用户一多,时时刻刻都在进行点赞,收藏等等处理,如果说我们采取传统的数据库的模式啊,这个交互量是非常大的,很难去抗住这个并发问题,所以我们......
  • 03PG缓存-写入写出
    一、缓存区的写入当后端进程想要访问一个页面时,会调用ReadBufferExtended,一般后端进程访问页面有三种情况页面在缓存池中页面不在缓存池中,从磁盘中将页面加载到缓存描述符空槽页面不在缓存池,从磁盘中将页面加载到缓存描述符受害者槽1.读取存储在缓存池中的页面以t1表为......
  • Redis 批量删除键
    Redis批量删除键1.简介在Redis中批量优雅的删除大量键是一个很麻烦的问题,下面例举常用的方法和优缺点。2.keys+del命令使用keys使用查找所有匹配的键,然后在使用del一起批量删除。优点操作简单。缺点查找性能特别差,需要遍历所有键。如果rediskey特别多,还有可能造......
  • 控制系统实现_分布式框架
    控制系统实现_分布式框架参考教程:http://www.autolabor.com.cn/book/ROSTutorials/https://www.bilibili.com/video/BV1Ci4y1L7ZZ/?spm_id_from=333.999.0.01.配置静态IP地址1.1配置树莓派静态IP地址当前分布式框架搭建时,树莓派是作为主机,而PC则作为从机,关于分布式框架的......
  • 【Redis 进阶】哨兵 Sentinel(重点理解流程和原理)
    Redis的主从复制模式下,一旦主节点由于故障不能提供服务,需要人工进行主从切换,同时大量的客户端需要被通知切换到新的主节点上,对于上了一定规模的应用来说,这种方案是无法接受的,于是Redis从2.8开始提供了RedisSentinel(哨兵)加个来解决这个问题。一、基本概念由于对Red......