本篇咱們記錄的內容是怎麼在Core中使用Redis 和 SQL Server 實現分佈式緩存。web
1、文章概念描述數據庫
分佈式緩存描述:緩存
分佈式緩存重點是在分佈式上,相信你們接觸過的分佈式有不少中,像分佈式開發,分佈式部署,分佈式鎖、事物、系統 等有不少。服務器
使咱們對分佈式自己就有一個很明確的認識,分佈式就是有多個應用程序組成,可能分佈在不一樣的服務器上,最終都是在爲web端提供服務。架構
分佈式緩存有如下幾點優勢:併發
(1)全部的Web服務器上的緩存數據都是相同的,不會由於應用程序不一樣,服務器的不一樣致使緩存數據的不同。異步
(2)緩存的是獨立的不受Web服務器的從新啓動或被刪除添加的影響,也就是說這些Web的改變不到致使緩存數據的改變。async
傳統的單體應用架構由於用戶的訪問量的不高,緩存的存在大多數都是存儲用戶的信息,以及一些頁面,大多數的操做都是直接和DB進行讀寫交互,這種架構簡單,也稱爲簡單架構,分佈式
傳統的OA項目好比ERP,SCM,CRM等系統由於用戶量不大也是由於大多數公司業務的緣由,單體應用架構仍是很經常使用的架構,可是有些系統隨着用戶量的增長,業務的擴張擴展,致使DB的瓶頸的出現。高併發
如下我所瞭解到的關於這種狀況的處理有如下兩種
(1):當用戶訪問量不大,可是讀寫的數據量很大的時候,咱們通常採起的是,對DB進行讀寫分離、一主多從、對硬件進行升級的方式來解決DB瓶頸的問題。
這樣的缺點也一樣純在:
一、用戶量大的時候怎麼辦?,
二、對於性能的提高有限,
三、性價比不高。提高一點性能就須要花費不少代價,(打個比方,如今的I/O吞吐量是0.9的須要提高到1.0,咱們在增長機器配置的狀況下這個價格確實很可觀的)
(2):當用戶訪問量也增長的時候,咱們就須要引入緩存了來解決了,一張圖描述緩存的大體的做用。
緩存主要針對的是不常常發生改變的而且訪問量很大的數據,DB數據庫能夠理解爲只做爲數據固化的或者只用來讀取常常發生改變的數據,上圖中我沒有畫SET的操做,就是想特地說明一下,緩存的存在能夠做爲一個臨時的數據庫,咱們能夠經過定時的任務的方式去同步緩存和數據庫中的數據,這樣作的好處是能夠轉移數據庫的壓力到緩存中。
緩存的出現解決了數據庫壓力的問題,可是當如下狀況發生的時候,緩存就不在起到做用了,緩存穿透、緩存擊穿、緩存雪崩這三種狀況。
緩存穿透:咱們的程序中用緩存的時候通常採起的是先去緩存中查詢咱們想要的緩存數據,若是緩存中不存在咱們想要的數據的話,緩存就失去了作用(緩存失效)咱們就是須要伸手向DB庫去要數據,這個時候這種動做過多數據庫就崩潰了,這種狀況須要咱們去預防了。
好比說:咱們向緩存獲取一個用戶信息,可是故意去輸入一個緩存中不存在的用戶信息,這樣就避過了緩存,把壓力從新轉移到數據上面了。
對於這種問題咱們能夠採起,把第一次訪問的數據進行緩存,由於緩存查不到用戶信息,數據庫也查詢不到用戶信息,這個時候避免重複的訪問咱們把這個請求緩存起來,把壓力從新轉向緩存中,有人會有疑問了,當訪問的參數有上萬個都是不重複的參數而且都是能夠躲避緩存的怎麼辦,咱們一樣把數據存起來設置一個較短過時時間清理緩存。
緩存擊穿:事情是這樣的,對於一些設置了過時時間的緩存KEY,在過時的時候,程序被高併發的訪問了(緩存失效),這個時候使用互斥鎖來解決問題,
互斥鎖原理:通俗的描述就是,一萬個用戶訪問了,可是隻有一個用戶能夠拿到訪問數據庫的權限,當這個用戶拿到這個權限以後從新建立緩存,這個時候剩下的訪問者由於沒有拿到權限,就原地等待着去訪問緩存。
永不過時:有人就會想了,我不設置過時時間不就好了嗎?能夠,可是這樣作也是有缺點的,咱們須要按期的取更新緩存,這個時候緩存中的數據比較延遲。
緩存雪崩:是指多種緩存設置了同一時間過時,這個時候大批量的數據訪問來了,(緩存失效)數據庫DB的壓力又上來了。解決方法在設置過時時間的時候在過時時間的基礎上增長一個隨機數儘量的保證緩存不會大面積的同事失效。
2、文章內容實現
在AspNetCore中使用 Redis實現緩存:
在項目中引用:using Microsoft.Extensions.Caching.Distributed; 使用IDistributedCache
IDistributedCache 接口
IDistributedCache接口包含同步和異步方法。 接口容許在分佈式緩存實現中添加、檢索和刪除項。 IDistributedCache接口包含如下方法:
採用字符串鍵並以byte[]形式檢索緩存項(若是在緩存中找到)。
使用字符串鍵向緩存添加項byte[]形式)。
根據鍵刷新緩存中的項,並重置其可調過時超時值(若是有)。
根據鍵刪除緩存項。
如下是個人代碼封裝DistributedCache類名:主要針對IDistributedCache中非異步方法,異步只寫了一個簡單的例子:
一、Get()獲取緩存
/// <summary>
/// 獲取緩存
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public object Get(string key)
{
string ReturnStr = "";
if (!string.IsNullOrEmpty(key))
{
if (Exists(key))
{
ReturnStr = Encoding.UTF8.GetString(_cache.Get(key));
}
}
return ReturnStr;
}
二、GetAsync()異步獲取緩存
/// <summary>
/// 使用異步獲取緩存信息
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public async Task<object> GetAsync(string key)
{
string ReturnString = null;
var value = await _cache.GetAsync(key);
if (value != null)
{
ReturnString = Encoding.UTF8.GetString(value);
}
return ReturnString;
}
三、Set()設置或添加緩存
/// <summary>
/// 添加緩存
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public bool Add(string key, object value)
{
byte[] val = null;
if (value.ToString() != "")
{
val = Encoding.UTF8.GetBytes(value.ToString());
}
DistributedCacheEntryOptions options = new DistributedCacheEntryOptions();
//設置絕對過時時間 兩種寫法
options.AbsoluteExpiration = DateTime.Now.AddMinutes(30);
// options.SetAbsoluteExpiration(DateTime.Now.AddMinutes(30));
//設置滑動過時時間 兩種寫法
options.SlidingExpiration = TimeSpan.FromSeconds(30);
//options.SetSlidingExpiration(TimeSpan.FromSeconds(30));
//添加緩存
_cache.Set(key, val, options);
//刷新緩存
_cache.Refresh(key);
return Exists(key);
}
四、Remove()刪除緩存
/// <summary>
/// 刪除緩存
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public bool Remove(string key)
{
bool ReturnBool = false;
if (key != "" || key != null)
{
_cache.Remove(key);
if (Exists(key) == false)
{
ReturnBool = true;
}
}
return ReturnBool;
}
五、修改緩存
/// <summary>
/// 修改緩存
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public bool Modify(string key, object value)
{
bool ReturnBool = false;
if (key != "" || key != null)
{
if (Remove(key))
{
ReturnBool = Add(key, value.ToString());
}
}
return ReturnBool;
}
六、驗證緩存是否存在
/// <summary>
/// 驗證是否存在
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public bool Exists(string key)
{
bool ReturnBool = true;
byte[] val = _cache.Get(key);
if (val == null || val.Length == 0)
{
ReturnBool = false;
}
return ReturnBool;
}
3、文章內容調用
以上是代碼的封裝:下面是在AspnetCore中調用
一、首先安裝Redis緩存:這個網上找個下載安裝就行
二、而後下載安裝:客戶端工具:RedisDesktopManager(方便管理)
三、在咱們的項目Nuget中 引用 Microsoft.Extensions.Caching.Redis
四、在項目啓動Setup.cs中註冊:Redis服務:代碼以下
public void ConfigureServices(IServiceCollection services)
{
//將Redis分佈式緩存服務添加到服務中
services.AddDistributedRedisCache(options =>
{
//用於鏈接Redis的配置 Configuration.GetConnectionString("RedisConnectionString")讀取配置信息的串
options.Configuration = "localhost";// Configuration.GetConnectionString("RedisConnectionString");
//Redis實例名RedisDistributedCache
options.InstanceName = "RedisDistributedCache";
});
services.AddMvc();
}
五、我是用CoreAPI來寫的 控制器中代碼以下:
1)先實例對象
private DistributedCache _Cache;
/// <summary>
/// 構造注入
/// </summary>
/// <param name="Cache"></param>
public ValuesController(IDistributedCache Cache)
{
_Cache = new DistributedCache(Cache);
}
2)調用DistributedCache 類中的方法 代碼以下:
[HttpGet("{id}")]
public string Get(int id)
{
//添加
bool booladd = _Cache.Add("id", "sssss");
//驗證
bool boolExists = _Cache.Exists("id");
//獲取
object obj = _Cache.Get("id");
//刪除
bool boolRemove = _Cache.Remove("id");
//修改
bool boolModify = _Cache.Modify("id", "ssssssss");
return obj.ToString();
}