如何使用redis实现分布式缓存

如何使用redis实现分布式缓存

分布式缓存描述:

分布式缓存重点是在分布式上,相信大家接触过的分布式有很多中,像分布式开发,分布式部署,分布式锁、事物、系统 等有很多。使我们对分布式本身就有一个很明确的认识,分布式就是有多个应用程序组成,可能分布在不同的服务器上,最终都是在为web端提供服务。
分布式缓存有以下几点优点:

  1. 所有的Web服务器上的缓存数据都是相同的,不会因为应用程序不同,服务器的不同导致缓存数据的不一样。

  2. 缓存的是独立的不受Web服务器的重新启动或被删除添加的影响,也就是说这些Web的改变不到导致缓存数据的改变。

传统的单体应用架构因为用户的访问量的不高,缓存的存在大多数都是存储用户的信息,以及一些页面,大多数的操作都是直接和DB进行读写交互,这种架构简单,也称为简单架构,
传统的OA项目比如ERP,SCM,CRM等系统因为用户量不大也是因为大多数公司业务的原因,单体应用架构还是很常用的架构,但是有些系统随着用户量的增加,业务的扩张扩展,导致DB的瓶颈的出现。

以下我所了解到的关于这种情况的处理有以下两种

(1):当用户访问量不大,但是读写的数据量很大的时候,我们一般采取的是,对DB进行读写分离、一主多从、对硬件进行升级的方式来解决DB瓶颈的问题。
这样的缺点也同样纯在:

1、用户量大的时候怎么办?,
2、对于性能的提升有限,
3、性价比不高。提升一点性能就需要花费很多代价,(打个比方,现在的I/O吞吐量是0.9的需要提升到1.0,我们在增加机器配置的情况下这个价格确实很可观的)

(2):当用户访问量也增加的时候,我们就需要引入缓存了来解决了,一张图描述缓存的大致的作用。

如何使用redis实现分布式缓存

缓存主要针对的是不经常发生改变的并且访问量很大的数据,DB数据库可以理解为只作为数据固化的或者只用来读取经常发生改变的数据,上图中我没有画SET的操作,就是想特意说明一下,缓存的存在可以作为一个临时的数据库,我们可以通过定时的任务的方式去同步缓存和数据库中的数据,这样做的好处是可以转移数据库的压力到缓存中。

缓存的出现解决了数据库压力的问题,但是当以下情况发生的时候,缓存就不在起到作用了,缓存穿透、缓存击穿、缓存雪崩这三种情况

缓存穿透:我们的程序中用缓存的时候一般采取的是先去缓存中查询我们想要的缓存数据,如果缓存中不存在我们想要的数据的话,缓存就失去了做用(缓存失效)我们就是需要伸手向DB库去要数据,这个时候这种动作过多数据库就崩溃了,这种情况需要我们去预防了。比如说:我们向缓存获取一个用户信息,但是故意去输入一个缓存中不存在的用户信息,这样就避过了缓存,把压力重新转移到数据上面了。对于这种问题我们可以采取,把名列前茅次访问的数据进行缓存,因为缓存查不到用户信息,数据库也查询不到用户信息,这个时候避免重复的访问我们把这个请求缓存起来,把压力重新转向缓存中,有人会有疑问了,当访问的参数有上万个都是不重复的参数并且都是可以躲避缓存的怎么办,我们同样把数据存起来设置一个较短过期时间清理缓存。

缓存击穿:事情是这样的,对于一些设置了过期时间的缓存KEY,在过期的时候,程序被高并发的访问了(缓存失效),这个时候使用互斥锁来解决问题,

互斥锁原理:通俗的描述就是,一万个用户访问了,但是只有一个用户可以拿到访问数据库的权限,当这个用户拿到这个权限之后重新创建缓存,这个时候剩下的访问者因为没有拿到权限,就原地等待着去访问缓存。

永不过期:有人就会想了,我不设置过期时间不就行了吗?可以,但是这样做也是有缺点的,我们需要定期的取更新缓存,这个时候缓存中的数据比较延迟。

缓存雪崩:是指多种缓存设置了同一时间过期,这个时候大批量的数据访问来了,(缓存失效)数据库DB的压力又上来了。解决方法在设置过期时间的时候在过期时间的基础上增加一个随机数尽可能的保证缓存不会大面积的同事失效。

项目准备

1、首先安装Redis
2、然后下载安装:客户端工具:RedisDesk较好Manager(方便管理)
3、在我们的项目Nuget中 引用 Microsoft.Extensions.Caching.Redis

为此我们新建一个ASP.NET Core MVC项目,在项目Startup类的ConfigureServices方法中先注册Redis服务:

public void ConfigureServices(IServiceCollection services){    //将Redis分布式缓存服务添加到服务中    services.AddDistributedRedisCache(options =>    {        //用于连接Redis的配置  Configuration.GetConnectionString("RedisConnectionString")读取配置信息的串        options.Configuration = "localhost";// Configuration.GetConnectionString("RedisConnectionString");        //Redis实例名DemoInstance        options.InstanceName = "DemoInstance";    });    services.AddMvc();}

也可以在上面注册Redis服务的时候,指定Redis服务器的IP地址、端口号和登录密码:

public void ConfigureServices(IServiceCollection services){    //将Redis分布式缓存服务添加到服务中    services.AddDistributedRedisCache(options =>    {        //用于连接Redis的配置  Configuration.GetConnectionString("RedisConnectionString")读取配置信息的串        options.Configuration = "192.168.1.105:6380,password=1qaz@WSX3edc$RFV";//指定Redis服务器的IP地址、端口号和登录密码        //Redis实例名DemoInstance        options.InstanceName = "DemoInstance";    });    services.AddMvc();}

后面我们会解释上面options.InstanceName设置的Redis实例名DemoInstance是用来做什么的

此外还可以在services.AddDistributedRedisCache方法中指定Redis服务器的超时时间,如果调用后面介绍的IDistributedCache接口中的方法,对Redis服务器进行的操作超时了,会抛出RedisConnectionException和RedisTimeoutException异常,所以下面我们在注册Redis服务的时候,指定了三个超时时间:

public void ConfigureServices(IServiceCollection services){    //将Redis分布式缓存服务添加到服务中    services.AddDistributedRedisCache(options =>    {        options.ConfigurationOptions = new StackExchange.Redis.ConfigurationOptions()        {            Password = "1qaz@WSX3edc$RFV",            ConnectTimeout = 5000,//设置建立连接到Redis服务器的超时时间为5000毫秒            SyncTimeout = 5000,//设置对Redis服务器进行同步操作的超时时间为5000毫秒            ResponseTimeout = 5000//设置对Redis服务器进行操作的响应超时时间为5000毫秒        };        options.ConfigurationOptions.EndPoints.Add("192.168.1.105:6380");        options.InstanceName = "DemoInstance";    });    services.AddMvc();}

其中ConnectTimeout是建立连接到Redis服务器的超时时间,而SyncTimeout和ResponseTimeout是对Redis服务器进行数据操作的超时时间。注意上面我们使用了options.ConfigurationOptions属性来设置Redis服务器的IP地址、端口号和登录密码

IDistributedCache 接口

在项目中引用:using Microsoft.Extensions.Caching.Distributed; 使用IDistributedCache

IDistributedCache接口包含同步和异步方法。 接口允许在分布式缓存实现中添加、检索和删除项。 IDistributedCache接口包含以下方法:
Get、 GetAsync
采用字符串键并以byte[]形式检索缓存项(如果在缓存中找到)。
Set、SetAsync
使用字符串键向缓存添加或更改项(byte[]形式)。
Refresh、RefreshAsync
根据键刷新缓存中的项,并重置其可调过期超时值(如果有)。
Remove、RemoveAsync
根据键删除缓存项。如果传入Remove方法的键在Redis中不存在,Remove方法不会报错,只是什么都不会发生而已,但是如果传入Remove方法的参数为null,则会抛出异常。

如上所述,由于IDistributedCache接口的Set和Get方法,是通过byte[]字节数组来向Redis存取数据的,所以从某种意义上来说不是很方便,下面我封装了一个RedisCache类,可以向Redis中存取任何类型的数据。

其中用到了Json.NET Nuget包,来做Json格式的序列化和反序列化:

using Microsoft.Extensions.Caching.Distributed;using Newtonsoft.Json;using System.Text;namespace AspNetCoreRedis.Assembly{    /// <summary>    /// RedisCache缓存操作类    /// </summary>    public class RedisCache    {        protected IDistributedCache cache;        /// <summary>        /// 通过IDistributedCache来构造RedisCache缓存操作类        /// </summary>        /// <param name="cache">IDistributedCache对象</param>        public RedisCache(IDistributedCache cache)        {            this.cache = cache;        }        /// <summary>        /// 添加或更改Redis的键值,并设置缓存的过期策略        /// </summary>        /// <param name="key">缓存键</param>        /// <param name="value">缓存值</param>        /// <param name="distributedCacheEntryOptions">设置Redis缓存的过期策略,可以用其设置缓存的绝对过期时间(AbsoluteExpiration或AbsoluteExpirationRelativeToNow),也可以设置缓存的滑动过期时间(SlidingExpiration)</param>        public void Set(string key, object value, DistributedCacheEntryOptions distributedCacheEntryOptions)        {            //通过Json.NET序列化缓存对象为Json字符串            //调用JsonConvert.SerializeObject方法时,设置ReferenceLoopHandling属性为ReferenceLoopHandling.Ignore,来避免Json.NET序列化对象时,因为对象的循环引用而抛出异常            //设置TypeNameHandling属性为TypeNameHandling.All,这样Json.NET序列化对象后的Json字符串中,会包含序列化的类型,这样可以保证Json.NET在反序列化对象时,去读取Json字符串中的序列化类型,从而得到和序列化时相同的对象类型            var stringObject = JsonConvert.SerializeObject(value, new JsonSerializerSettings()            {                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,                TypeNameHandling = TypeNameHandling.All            });            var bytesObject = Encoding.UTF8.GetBytes(stringObject);//将Json字符串通过UTF-8编码,序列化为字节数组            cache.Set(key, bytesObject, distributedCacheEntryOptions);//将字节数组存入Redis            Refresh(key);//刷新Redis        }        /// <summary>        /// 查询键值是否在Redis中存在        /// </summary>        /// <param name="key">缓存键</param>        /// <returns>true:存在,false:不存在</returns>        public bool Exist(string key)        {            var bytesObject = cache.Get(key);//从Redis中获取键值key的字节数组,如果没获取到,那么会返回null            if (bytesObject == null)            {                return false;            }            return true;        }        /// <summary>        /// 从Redis中获取键值        /// </summary>        /// <typeparam name="T">缓存的类型</typeparam>        /// <param name="key">缓存键</param>        /// <param name="isExisted">是否获取到键值,true:获取到了,false:键值不存在</param>        /// <returns>缓存的对象</returns>        public T Get<T>(string key, out bool isExisted)        {            var bytesObject = cache.Get(key);//从Redis中获取键值key的字节数组,如果没获取到,那么会返回null            if (bytesObject == null)            {                isExisted = false;                return default(T);            }            var stringObject = Encoding.UTF8.GetString(bytesObject);//通过UTF-8编码,将字节数组反序列化为Json字符串            isExisted = true;            //通过Json.NET反序列化Json字符串为对象            //调用JsonConvert.DeserializeObject方法时,也设置TypeNameHandling属性为TypeNameHandling.All,这样可以保证Json.NET在反序列化对象时,去读取Json字符串中的序列化类型,从而得到和序列化时相同的对象类型            return JsonConvert.DeserializeObject<T>(stringObject, new JsonSerializerSettings()            {                TypeNameHandling = TypeNameHandling.All            });        }        /// <summary>        /// 从Redis中删除键值,如果键值在Redis中不存在,该方法不会报错,只是什么都不会发生        /// </summary>        /// <param name="key">缓存键</param>        public void Remove(string key)        {            cache.Remove(key);//如果键值在Redis中不存在,IDistributedCache.Remove方法不会报错,但是如果传入的参数key为null,则会抛出异常        }        /// <summary>        /// 从Redis中刷新键值        /// </summary>        /// <param name="key">缓存键</param>        public void Refresh(string key)        {            cache.Refresh(key);        }    }}

使用测试

然后我们在ASP.NET Core MVC项目中,新建一个CacheController,然后在其Index方法中来测试RedisCache类的相关方法:

public class CacheController : Controller{    protected RedisCache redisCache;    //由于我们前面在Startup类的ConfigureServices方法中调用了services.AddDistributedRedisCache来注册Redis服务,所以ASP.NET Core MVC会自动依赖注入下面的IDistributedCache cache参数    public CacheController(IDistributedCache cache)    {        redisCache = new RedisCache(cache);    }    public IActionResult Index()    {        bool isExisted;        isExisted = redisCache.Exist("abc");//查询键值"abc"是否存在        redisCache.Remove("abc");//删除不存在的键值"abc",不会报错        string key = "Key01";//定义缓存键"Key01"        string value = "This is a demo key !";//定义缓存值        redisCache.Set(key, value, new DistributedCacheEntryOptions()        {            AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)        });//设置键值"Key01"到Redis,使用绝对过期时间,AbsoluteExpirationRelativeToNow设置为当前系统时间10分钟后过期        //也可以通过AbsoluteExpiration属性来设置绝对过期时间为一个具体的DateTimeOffset时间点        //redisCache.Set(key, value, new DistributedCacheEntryOptions()        //{        //    AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(10)        //});//设置键值"Key01"到Redis,使用绝对过期时间,AbsoluteExpiration设置为当前系统时间10分钟后过期        var getVaue = redisCache.Get<string>(key, out isExisted);//从Redis获取键值"Key01",可以看到getVaue的值为"This is a demo key !"        value = "This is a demo key again !";//更改缓存值        redisCache.Set(key, value, new DistributedCacheEntryOptions()        {            SlidingExpiration = TimeSpan.FromMinutes(10)        });//将更改后的键值"Key01"再次缓存到Redis,这次使用滑动过期时间,SlidingExpiration设置为10分钟        getVaue = redisCache.Get<string>(key, out isExisted);//再次从Redis获取键值"Key01",可以看到getVaue的值为"This is a demo key again !"        redisCache.Remove(key);//从Redis中删除键值"Key01"        return View();    }}

前面我们在项目的Startup类ConfigureServices方法中,调用services.AddDistributedRedisCache注册Redis服务的时候,有设置options.InstanceName = “DemoInstance”,那么这个InstanceName到底有什么用呢?

当我们在上面的CacheController中调用Index方法的下面代码后:

string key = "Key01";//定义缓存键"Key01"string value = "This is a demo key !";//定义缓存值redisCache.Set(key, value, new DistributedCacheEntryOptions(){    AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)});//设置键值"Key01"到Redis,使用绝对过期时间,AbsoluteExpirationRelativeToNow设置为当前系统时间10分钟后过期

我们使用redis-cli登录到Redis服务器中,使用Keys *指令查看当前Redis服务中存储的所有键时,可以看到结果如下:

如何使用redis实现分布式缓存

可以看到虽然我们代码中向Redis存入的键是”Key01″,但是实际上在Redis服务中存储的键是”DemoInstanceKey01″,所以实际上真正存入Redis服务中的键是“InstanceName+键”这种组合键,因此我们可以通过设置不同的InstanceName来为不同的Application在Redis中做数据隔离,这就是InstanceName的作用

读到这里,这篇“如何使用redis实现分布式缓存”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注亿速云行业资讯频道。

文章标题:如何使用redis实现分布式缓存,发布者:亿速云,转载请注明出处:https://worktile.com/kb/p/24824

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
亿速云的头像亿速云认证作者
上一篇 2022年9月13日 下午11:50
下一篇 2022年9月14日 上午11:01

相关推荐

  • Word首页不显示页码怎么设置

    首页不显示页码设置方法: 1、首先打开word文档。 2、然后点击上面菜单栏中的“插入”。 3、之后去点击功能里面的“页码”。 4、最后先去选择当前的页码, 然后点击右侧的删除键即可。 到此,相信大家对“Word首页不显示页码怎么设置”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关…

    2022年8月31日
    1.2K00
  • MySQL中数据库优化的常见sql语句有哪些

    1.SHOW ENGINES 查看执行引擎以及默认引擎。 2.SHOW PROCESSLIST SHOW PROCESSLIST查看当前数据库连接的使用情况,以及各种状态信息,非常有用。SHOW PROCESSLIST; 只列出前100条,如果想全列出请使用SHOW FULL PROCESSLIST…

    2022年9月1日
    39400
  • MySQL中replace into与replace区别是什么

    0.故事的背景 【表格结构】 CREATE TABLE `xtp_algo_white_list` ( `strategy_type` int DEFAULT NULL, `user_name` varchar(64) COLLATE utf8_bin DEFAULT NULL, `status` …

    2022年9月1日
    73200
  • excel下拉列表怎么添加新内容

    excel下拉列表添加新内容的方法: 1、说先点击添加好的下拉,查看里面的内容。 2、之后选择这个下拉,点击任务栏的“数据”。 3、之后在点击下面的“数据验证”。 4、在弹出的窗口中进入设置,在“来源”中添加要新增的内容, 最后点击确定即可。 以上就是“excel下拉列表怎么添加新内容”这篇文章的所…

    2022年8月27日
    1.2K00
  • MySQL中的join语句算法如何优化

    一、join语句算法 创建两个表t1和t2 CREATE TABLE `t2` ( `id` int(11) NOT NULL, `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `a` (`a`)…

    2022年9月16日
    57100
  • windows浩辰看图王cad文件无法打印怎么解决

    解决方法: 方法一: 1、软件名列前茅次打开会更新一个修复打印功能的补丁。 2、因此如果我们出现无法打印问题,只要重启一下软件即可解决。 3、重启后如果还不能打印,那么可能是软件本身问题,需要重新下载安装。 方法二: 1、如果重启或重装软件也无法解决,那么可能是没有连接打印机。 2、我们可以确认一下…

    2022年9月20日
    51600
  • 如何实现C++程序释放后使用导致的漏洞分析

    1、释放后使用 当动态分配的内存释放时,该内存的内容是不确定的,有可能保持完整并可以被访问,因为什么时候重新分配或回收释放的内存块是内存管理程序决定的,但是,也可能该内存的内容已经被改变,导致意外的程序行为。因此,当内存释放之后,保证不再对它进行写入或读取。 2、 释放后使用的危害 由内存管理不当导…

    2022年9月20日
    52600
  • 影城服务器续保走什么科目

    影城服务器续保一般走固定资产和管理费用这两个科目。首先要确定的是服务器是否被列入固定资产。如果是,则续保费用可以计入固定资产的后续支出中。此外,如果服务器的续保被视作一种常规支出,那么它也可以作为管理费用的一部分进行核算,具体归在管理费用下的维修保养费科目。 一、固定资产的后续支出 当影城服务器属于…

    2024年4月25日
    700
  • 电脑驱动位置如何查看

    电脑驱动查看方法: 1、首先我们点击任务栏中的搜索按钮。 2、打开搜索栏后,在其中搜索并打开“设备管理器” 3、双击打开想要查看的电脑驱动程序。 4、然后点击上方的“驱动程序”选项卡。 5、点击“驱动程序详细信息” 6、然后在图示位置就可以看到电脑驱动信息了。 7、如果你觉得操作比较麻烦可以尝试使用…

    2022年9月21日
    2.0K00
  • Redis如何实现排行榜及相同积分按时间排序功能

    在日常的开发中,经常会碰到需要对用户的分值等进行排序,比如在游戏里面需要对战斗力进行排行,在组队活动中需要对各个队伍的贡献值进行排行,在微信中需要对各个好友的步数进行排行,此时一般会选择redis的有序集合对用户的分数进行存储,从而实现排行榜的需求,但是不同的场景排行榜的方式也略有不同,以下根据自己…

    2022年8月30日
    1.0K00
注册PingCode 在线客服
站长微信
站长微信
电话联系

400-800-1024

工作日9:30-21:00在线

分享本页
返回顶部