简介以及区别

ASP.NET Core 缓存Caching,.NET Core 中为我们提供了Caching 的组件。

目前Caching 组件提供了三种存储方式。

Memory

Redis

SqlServer

1.MemoryCache

Cache是一个绝大多数项目会用到的一个技术 为了减少磁盘的读取次数,提高程序性能,将频繁读取的配置文件缓存到内存中,加速配置的读取。并且,在磁盘的配置文件更改后,更新缓存

  1. 绝对过期支持
  2. 滑动过期支持(指定一个时间,TimeSpan,指定时间内有被Get缓存时间则顺延,否则过期)
  3. 过期回调
  4. 自定义过期

仓库地址是:https://github.com/aspnet/Caching

IMemoryCache,它表示存储在 Web 服务器内存中的缓存,内存缓存可以存储任何对象,存储形式键值对

Memorycache无法进行持久化

Memorycache只支持简单的key/value数据结构

Memorycache 是多线程  速度快

2.Redis

简单来说 redis 就是一个数据库,不过与传统数据库不同的是 redis 的数据是存在内存中的,所以读写速度非常快,因此 redis 被广泛应用于缓存方向。另外,redis 也经常用来做分布式锁。redis 提供了多种数据类型来支持不同的业务场景。除此之外,redis 支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方案。

Redis是一个支持持久化的内存数据库,通过持久化机制把内存中的数据同步到硬盘文件来保证数据持久化 ,当Redis重启后通过把硬盘文件重新加载到内存,就能达到恢复数据的目的。

如果你对数据持久化和数据同步有所要求,那么推荐你选择Redis,因为这两个特性Memcached都不具备。即使你只是希望在升级或者重启系统后缓存数据不会丢失,选择Redis也是明智的。

Redis支持数据类型比如key-value string  list hash zset 等

Redis缓存数据库(多个)   Redis缓存文件夹(多个目录)

Redis只能使用单线程,性能受限于CPU性能

区别

对于 redis 和 memcached 我总结了下面四点。现在公司一般都是用 redis 来实现缓存,而且 redis 自身也越来越强大了!

  1、redis支持更丰富的数据类型(支持更复杂的应用场景):Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。memcache支持简单的数据类型,String。

  2、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而Memecache把数据全部存在内存之中。

  3、集群模式:memcached没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 redis 目前是原生支持 cluster 模式的.

  4、Memcached是多线程,非阻塞IO复用的网络模型;Redis使用单线程的多路 IO 复用模型。

应用场景

redis:数据量较小的更性能操作和运算上。

memcache:用于在动态系统中减少数据库负载,提升性能;做缓存,提高性能(适合读多写少,对于数据量比较大,可以采用sharding)。

1.MemoryCache使用

Session缓存和Cache缓存的区别如下:

(1)最大的区别是Cache提供缓存依赖来更新数据,而Session只能依靠定义的缓存时间来判断缓存数据是否有效。

(2)即使应用程序终止,只要Cache.Add方法中定义的缓存时间未过期,下次开启应用程序时,缓存的数据依然存在。而Session缓存只是存在于一次会话中,会话结束后,数据也就失效了。

(3)Session容易丢失,导致数据的不确定性,而Cache不会出现这种情况。

(4)由于Session是每次会话就被加载,所以不适宜存放大量信息,否则会导致服务器的性能降低。而Cache则主要用来保存大容量信息,如数据库中的多个表。

需要特别注意:为了提高Cache的有效利用率,建议对于不经常改动的数据使用Cache。

public void ConfigureServices(IServiceCollection services)
{  
//如何处理session
services.AddSession();
//memoryCache
services.AddMemoryCache(); //.......
} public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//启用session
app.UseSession();
app.UseRouting();
//......
}

public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
// Add framework services. }
private IMemoryCache _cache;

        public LongLongController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}

1、方法:TryGetValue 及 方法缓存的存取(在TryGetValue 中,Out 参数的类型应与缓存中存储的值的类型一致。否则TryGetValue 中的Out参数永远为NULL。)

public IActionResult Index()
{
string cacheKey_2 = "CacheKey";
List<string> cacheEntry;
//如果缓存没有过期,则Out测试就是缓存存储的值,注意存放值的类型应该和Out参数一致。
var bol = _cache.TryGetValue<List<string>>(cacheKey_2, out cacheEntry);
//判断缓存是否存在
if (!bol)
{
List<string> lst = new List<string>() { "陈大六", "陈卧龙", "陈新宇", "刘丽", "花国锋" };
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(10));
_cache.Set(cacheKey_2, lst, cacheEntryOptions);
}
ViewBag.cacheEntry = _cache.Get(cacheKey_2);
return View();
}

2、设置缓存的过期时间,可采用绝对过期时间或相对过期时间两种模式;

相对过期时间设置方式:

var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromSeconds(10));

注意上述代码中的备注;假设我们设置一个缓存的相对过期时间为10秒,缓存由A创建,十秒中内,B进入可系统,并读取了缓存,这时缓存的有效时间又会变成十秒,同理,只要缓存在十秒钟内不间断有其他人访问,则缓存永远不会过期。如果大于十秒,则缓存过期。

绝对过期时间设置方式:

 var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromSeconds(10));

绝对过期时间不管期间有没有人访问,在时间过后,就会过期,清空。

3、移除缓存

_cache.Remove(cacheKey_2);

4、缓存的优先级分为四种:永不过期  大于  高优先级  大于  一般优先级  大于  低优先级。

/缓存优先级 (程序压力大时,会根据优先级自动回收)

5、缓存过期后,执行回调函数,

public IActionResult cacheCallback()
{
List<string> lst = new List<string>() { "陈大六", "陈卧龙", "陈新宇", "刘丽", "花国锋" };
//缓存回调 10秒过期会回调
string cacheKey = "cacheKey";
_cache.Set(cacheKey, lst, new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromSeconds(10))
.RegisterPostEvictionCallback((key, value, reason, substate) =>
{
//调用回调函数
GetIntList(key,value,reason,substate);
}));
//
return View();
}
public void GetIntList(object key,object value, object reason, object substate)
{
List<string> lst=(List<string>)value;
//说白了就是被释放了
Console.WriteLine($"键{key}的缓存因:{reason}而改变。");
foreach(var item in lst)
{
Console.WriteLine(item);
}
}

6、缓存回调 根据Token过期

public ActionResult CacheToken()
{
List<string> lst = new List<string>() { "陈大六", "陈卧龙", "陈新宇", "刘丽", "花国锋" };
string cacheKey = "CacheToken";
//缓存回调 根据Token过期
var cts = new CancellationTokenSource();
_cache.Set(cacheKey, lst, new MemoryCacheEntryOptions()
.AddExpirationToken(new CancellationChangeToken(cts.Token))
.RegisterPostEvictionCallback((key, value, reason, substate) =>
{
Console.WriteLine($"键{key}值{value}改变,因为{reason}");
}));
cts.Cancel(); //执行到Cancel()方法时,会执行回调删除
return View();
}

2.Redis下载

redis安装包:https://github.com/microsoftarchive/redis/releases

redis客户端:https://redisdesktop.com/download  最新的要付费可以使用旧版

https://github.com/uglide/RedisDesktopManager/releases/tag/0.8.8

为什么要用 redis?/为什么要用缓存?
  主要从“高性能”和“高并发”这两点来看待这个问题。

高性能:

假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!

高并发:

直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。

redis 的线程模型
  redis 内部使用文件事件处理器 file event handler,这个文件事件处理器是单线程的,所以 redis 才叫做单线程的模型。它采用 IO 多路复用机制同时监听多个 socket,根据 socket 上的事件来选择对应的事件处理器进行处理。

  文件事件处理器的结构包含 4 个部分:

  多个 socket
  IO 多路复用程序
  文件事件分派器
  事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
  多个 socket 可能会并发产生不同的操作,每个操作对应不同的文件事件,但是 IO 多路复用程序会监听多个 socket,会将 socket 产生的事件放入队列中排队,事件分派器每次从队列中取出一个事件,把该事件交给对应的事件处理器进行处理。

3.StackExchange.Redis

在NuGet上安装StackExchange.Redis,然后在appsettings.json文件里面添加Redis相关配置信息:

{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"Redis": {
"Default": {
"Connection": "127.0.0.1:6379",
"InstanceName": "local",
"DefaultDB": 8
}
}
}

帮助类

using StackExchange.Redis;
using System;
using System.Collections.Concurrent; namespace RedisDemo
{
public class RedisHelper : IDisposable
{
//连接字符串
private string _connectionString;
//实例名称
private string _instanceName;
//默认数据库
private int _defaultDB;
private ConcurrentDictionary<string, ConnectionMultiplexer> _connections;
public RedisHelper(string connectionString, string instanceName, int defaultDB = 0)
{
_connectionString = connectionString;
_instanceName = instanceName;
_defaultDB = defaultDB;
_connections = new ConcurrentDictionary<string, ConnectionMultiplexer>();
} /// <summary>
/// 获取ConnectionMultiplexer
/// </summary>
/// <returns></returns>
private ConnectionMultiplexer GetConnect()
{
return _connections.GetOrAdd(_instanceName, p => ConnectionMultiplexer.Connect(_connectionString));
} /// <summary>
/// 获取数据库
/// </summary>
/// <param name="configName"></param>
/// <param name="db">默认为0:优先代码的db配置,其次config中的配置</param>
/// <returns></returns>
public IDatabase GetDatabase()
{
return GetConnect().GetDatabase(_defaultDB);
} public IServer GetServer(string configName = null, int endPointsIndex = 0)
{
var confOption = ConfigurationOptions.Parse(_connectionString);
return GetConnect().GetServer(confOption.EndPoints[endPointsIndex]);
} public ISubscriber GetSubscriber(string configName = null)
{
return GetConnect().GetSubscriber();
} public void Dispose()
{
if (_connections != null && _connections.Count > 0)
{
foreach (var item in _connections.Values)
{
item.Close();
}
}
}
}
}

在Startup.cs类的ConfigureServices方法里面添加服务注入:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; namespace RedisDemo
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
} public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//redis缓存
var section = Configuration.GetSection("Redis:Default");
//连接字符串
string _connectionString = section.GetSection("Connection").Value;
//实例名称
string _instanceName = section.GetSection("InstanceName").Value;
//默认数据库
int _defaultDB = int.Parse(section.GetSection("DefaultDB").Value ?? "0");
services.AddSingleton(new RedisHelper(_connectionString, _instanceName, _defaultDB));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} app.UseMvc();
}
}
}

新建一个控制器,然后通过构造函数注入:

using Microsoft.AspNetCore.Mvc;
using StackExchange.Redis; namespace RedisDemo.Controllers
{
[Route("api/redis")]
[ApiController]
public class RedisController : ControllerBase
{
private readonly IDatabase _redis;
public RedisController(RedisHelper client)
{
_redis = client.GetDatabase();
} [HttpGet]
public string Get()
{
// 往Redis里面存入数据
_redis.StringSet("Name", "Tom");
// 从Redis里面取数据
string name = _redis.StringGet("Name");
return name;
}
}
}

CSRedisCore

在NuGet上安装CSRedisCore,然后在appsettings.json文件里面添加Redis相关配置信息:

{
"RedisServer": {
"Cache": "192.168.0.3:6379,password=redis,preheat=5,idleTimeout=600,defaultDatabase=13,prefix=Cache"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Trace",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

新建一个 IRedisClient 接口

1 public interface IRedisClient
2 {
3 string Get(string key);
4 Task<string> GetAsync(string key);
5 void Set(string key, object t, int expiresSec = 0);
6 Task SetAsync(string key, object t, int expiresSec = 0);
7 T Get<T>(string key) where T : new();
8 Task<T> GetAsync<T>(string key) where T : new();
9 }

实现接口

 1 public class CustomerRedis : IRedisClient
2 {
3 public string Get(string key)
4 {
5 return RedisHelper.Get(key);
6 }
7
8 public T Get<T>(string key) where T : new()
9 {
10 return RedisHelper.Get<T>(key);
11 }
12
13 public async Task<string> GetAsync(string key)
14 {
15 return await RedisHelper.GetAsync(key);
16 }
17
18 public async Task<T> GetAsync<T>(string key) where T : new()
19 {
20 return await RedisHelper.GetAsync<T>(key);
21 }
22
23 public void Set(string key, object t, int expiresSec = 0)
24 {
25 RedisHelper.Set(key, t, expiresSec);
26 }
27
28 public async Task SetAsync(string key, object t, int expiresSec = 0)
29 {
30 await RedisHelper.SetAsync(key, t, expiresSec);
31 }
32 }

Startup

1 services.AddScoped<IRedisClient, CustomerRedis>();
2
3 var redisConn = Configuration["Cache:RedisConnection"];
4 //services.Configure<WeChatPayOptions>(Configuration.GetSection("WeChatPay"));
5 var csredis = new CSRedis.CSRedisClient(redisConn);
6 RedisHelper.Initialization(csredis);

调用

 1 private readonly IRedisClient _redisClient;
2 public ValuesController(IRedisClient redisClient)
3 {
4 this._redisClient = redisClient;
5 }
6
7 [HttpGet("test")]
8 public async Task<ActionResult> Test()
9 {
10 await this._redisClient.SetAsync("test", "test",100);
11 var res = await this._redisClient.GetAsync("test");
12 return Ok(res);
13 }

github地址:https://github.com/2881099/csredis

NET 5 MemoryCache与Redis使用以及StackExchange.Redis和CSRedisCore的更多相关文章

  1. 【11】Redis .net 实例 StackExchange.Redis框架

    1.创建测试项目并下载nuget包:StackExchange.Redis PM> Install-Package StackExchange.Redis 2.创建 RedisHelper类 p ...

  2. Redis系列(一)StackExchange.Redis的使用

    Redis系列(一)StackExchange.Redis的使用 一.DLL安装 用NuGet搜索StackExchange.Redis,然后下载就可以. ConnectionMultiplexer对 ...

  3. Redis集群~StackExchange.redis连接Twemproxy代理服务器

    回到目录 本文是Redis集群系列的一篇文章,主要介绍使用StackExchange.Redis进行Twemproxy(文中简称TW)代理服务的连接过程,事务上,对于TW来说,我们需要理解一下它的物理 ...

  4. Redis 系列 (一) StackExchange.Redis的使用

    一.DLL安装 用NuGet搜索StackExchange.Redis,然后下载就可以. ConnectionMultiplexer对象是StackExchange.Redis最中枢的对象.这个类的实 ...

  5. Redis学习笔记~StackExchange.Redis实现分布式Session

    回到目录 对于多WEB的环境现在已经是必须的了,很难想像一台WEB服务器面对百万并发的响应,所以,我们需要多台WEB服务器集群合作,来缓解这种高并发,高吞吐的场景,而对于多WEB的场景又会有个问题出现 ...

  6. Redis集群~StackExchange.redis连接Sentinel服务器并订阅相关事件(原创)

    回到目录 对于redis-sentinel我在之前的文章中已经说过,它是一个仲裁者,当主master挂了后,它将在所有slave服务器中进行选举,选举的原则当然可以看它的官方文章,这与我们使用者没有什 ...

  7. Redis集群~StackExchange.Redis(10月6号版1.1.608.0)连接Twemproxy支持Auth指令了

    回到目录 对于StackExchange.Redis这个驱动来说,之前的版本在使用Proxy为Twemproxy代理时,它是不支持Password属性的,即不支持原始的Auth指令,而我也修改过源代码 ...

  8. 关于 Senparc.Weixin.Cache.Redis 引用的 StackExchange.Redis 版本不匹配的反馈测试

    推测原因是老系统中有地方引用了旧版本的 StackExchange.Redis,原因是 StackExchange.Redis 1.2.6 版本未提供针对 .net 4.6 以上的支持,导致库引用会失 ...

  9. Windows下Redis缓存服务器的使用 .NET StackExchange.Redis Redis Desktop Manager

    Redis缓存服务器是一款key/value数据库,读110000次/s,写81000次/s,因为是内存操作所以速度飞快,常见用法是存用户token.短信验证码等 官网显示Redis本身并没有Wind ...

随机推荐

  1. Django踩坑记录3

    路径如下: admin.py的代码: from django.contrib import admin from sign.models import Event,Guest # Register y ...

  2. Linux 学习笔记01丨Ubuntu系统安装、配置及软件教程集合

    1. Ubuntu系统安装 Windows10安装ubuntu18.04双系统教程 Ubuntu 20.04.1 镜像下载 软碟通 机械革命进入BIOS模式 要按F2,注意将Boot Option中将 ...

  3. 【数据结构】关于前缀树(单词查找树,Trie)

    前缀树的说明和用途 前缀树又叫单词查找树,Trie,是一类常用的数据结构,其特点是以空间换时间,在查找字符串时有极大的时间优势,其查找的时间复杂度与键的数量无关,在能找到时,最大的时间复杂度也仅为键的 ...

  4. Dotnet Core下的Channel, 你用了吗?

    今天给大家分享一个微软官方的好东西:Channel.   前言 今天给大家分享一个微软官方的生产者/消费者方案的特性解决:Channel. Channel在System.Threading.Chann ...

  5. Prafab Varient 预制体变体

    预制体与类的类比思维:     预制体相当于一个类,当它应用到场景当中,就是一个实例. 类的继承特性也充分运用到预制体中,即预制体变体. 相似预制体的需求场景:         例子1:多个游戏的窗口 ...

  6. python基本输入输出函数

    python程序设计中有三个重要的基本输入.输出函数,用于输入.转换和输出,分别是input(),eval(),print() 1,input()函数 """ input ...

  7. 从0开始带你成为JVM实战高手(百度网盘)

    狸猫技术窝<从0开始带你成为JVM实战高手> 之前写过几篇 JVM 相关的文章,最近复盘的时候,发现狸猫技术窝<从0开始带你成为JVM实战高手>真的不错,然后就在网上找了一下( ...

  8. 去除openwrite.cn博客验证码限制

    相信有的小伙伴肯定遇到过如下这种情况,但是作为老白嫖党肯定是 「下次一定」 了,所以今天我们来看看如何不关注公众号实现 「阅读原文」. 如何解决呢? 1.通过 F12 打开控制台,切换至 Elemen ...

  9. JavaScript使用中的一些小技巧

    任何一门技术在实际中都会有一些属于自己的小技巧.同样的,在使用JavaScript时也有一些自己的小技巧,只不过很多时候有可能容易被大家忽略.而在互联网上,时不时的有很多同行朋友会总结(或收集)一些这 ...

  10. 《MySQL慢查询优化》之SQL语句及索引优化

    1.慢查询优化方式 服务器硬件升级优化 Mysql服务器软件优化 数据库表结构优化 SQL语句及索引优化 本文重点关注于SQL语句及索引优化,关于其他优化方式以及索引原理等,请关注本人<MySQ ...