NET 5 MemoryCache與Redis使用以及StackExchange.Redis和CSRedisCore


簡介以及區別

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、緩存的優先級分為四種:永不過期  大於  高優先級  大於  一般優先級  大於  低優先級。

/緩存優先級 (程序壓力大時,會根據優先級自動回收)
View Code

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM