.net使用CSRedis操作Redis緩存的簡單筆記(新手教程)
0.介紹
.NET Core or .NET Framework 4.0+ client for Redis and Redis Sentinel (2.8) and Cluster. Includes both synchronous and asynchronous clients.
本文記錄CSRedis在開發過程中的簡單使用,可以直接調試樣例源碼。
1. 參考資料
github //github.com/2881099/csredis
Redis Runoob教程 //www.runoob.com/redis/redis-install.html
2.核心內容
-
使用心得
1.科學使用緩存
- 從Redis中讀取數據
從Redis中讀取數據需要考慮”數據存在,但是Redis中過期或者未寫入的情況”這時候就需要根據指定Key先獲取數據再寫入Redis中。
- 將數據寫入Redis
寫入Redis需要增加過期時。增加過期時間的時候可以將時間隨機,這樣可以避免緩存在相同時間過期而引發緩存雪崩。
在高並發的情況下,如果根據key獲取的數據不存在,也將null保存至Redis中,而非抽象類(AbstractRedisService)中做刪除動作,這樣可避免緩存穿透。
我對高並發場景下的緩存使用理解不深,AbstractRedisService抽象類在更新失效值的時候使用了lock,使用lock寫法容易造成堵塞,但是如果不使用lock的話,就會出現重複讀取再寫入Redis的情況,並且在當前情況下如何處理緩存擊穿也不是很清楚,希望大家能不吝賜教~
2.在使用CSRedis的時候遇到了多個業務模塊都有相識的代碼,於是抽取了抽象類AbstractRedisService,在業務模塊實現的時候只需要實現RedisGroup 屬性與GetKeyByRedisInputKey、GetValueByKey兩個方法。
3.在實際應用中,有可能會出現程序與Redis服務連接不穩定的情況,如果Redis服務沒有發現問題的話,可以嘗試使用下面三種方式解決(參考 //github.com/2881099/csredis/issues)
-
connectTimeout=30000 設置連接超時時間
-
tryit=3 設置重試次數
-
preheat=100 預熱連接數
-
初始化RedisHelper
// 初始化RedisHelper
RedisHelper.Initialization(CSRedisInstance.GetRedis());
/// <summary>
/// CSRedisClient 單例
/// </summary>
internal class CSRedisInstance
{
private static readonly object _lock = new object();
private static CSRedisClient _csRedis = null;
private const string ip = "127.0.0.1";
private const string port = "6379";
private const string preheat = "100"; // 設置預熱連接數
private const string connectTimeout = "100"; // 設置連接超時時間
private const string tryit = "1"; // 設置重試次數
private const string prefix = "CSRedisTest."; // 設置前綴
private static readonly string _connectString = $"{ip}:{port}," +
$"preheat={preheat},connectTimeout={connectTimeout},tryit={tryit},prefix={prefix}";
internal static CSRedisClient GetRedis()
{
if (_csRedis == null)
{
lock (_lock)
{
if (_csRedis == null)
{
_csRedis = GetCSRedisClient();
}
}
}
return _csRedis;
}
private static CSRedisClient GetCSRedisClient()
{
return new CSRedisClient(_connectString);
}
}
-
業務應用 – 抽象類分享
AbstractRedisService抽象類
/// <summary>
/// Redis抽象類 - 緩存內容根據Key指定刷新
/// </summary>
/// <typeparam name="RedisInputKey">輸入Key值</typeparam>
/// <typeparam name="RedisValue">Redis保存的Value值</typeparam>
public abstract class AbstractRedisService<RedisInputKey, RedisValue>
{
private readonly static object _lock = new object();
private const int _expireTime = 3600;
/// <summary>
/// 緩存模塊
/// </summary>
protected abstract RedisGroup CacheGroup { get; }
/// <summary>
/// 根據輸入Key值,返回真正RedisKey
/// </summary>
protected abstract string GetKeyByRedisInputKey(RedisInputKey redisInputKey);
/// <summary>
/// 根據輸入Key值,獲取對應Value
/// </summary>
protected abstract RedisValue GetValueByKey(RedisInputKey redisInputKey);
public RedisValue GetRedisByRedisInputKey(RedisInputKey redisInputKey)
{
if (!RedisControl.UseRedis())
return default(RedisValue);
var result = GetRedisValue(redisInputKey);
// 刷新Redis之後還無法獲取正確的值,則記錄原因
if (result == null)
{
// 日誌輸出
};
return result;
}
public void NoticeRedisUpdateByKey(RedisInputKey redisInputKey)
{
try
{
UpdateByKey(redisInputKey);
}
catch (Exception e)
{
// 日誌輸出
}
}
/// <summary>
/// 有可能沒有Redis服務,則將異常捕捉,並停止使用Redis緩存
/// </summary>
private RedisValue GetRedisValue(RedisInputKey redisInputKey)
{
RedisValue value = default(RedisValue);
string key = GetKeyByRedisInputKey(redisInputKey);
try
{
value = GetRedisValueByKey(key);
if (value != null)
return value;
lock (_lock)
{
value = GetRedisValueByKey(key);
if (value == null)
UpdateByKey(redisInputKey);
}
value = GetRedisValueByKey(key);
}
catch (Exception e)
{
RedisControl.StopUseRedis();
// 日誌輸出
}
return value;
}
private void UpdateByKey(RedisInputKey redisInputKey)
{
var key = GetKeyByRedisInputKey(redisInputKey);
RedisValue value = GetValueByKey(redisInputKey);
if (value == null) //刪除操作執行更新時,移除掉key
RedisHelper.Del(key);
else
RedisHelper.Set(key, value, _expireTime + 200 * new Random().Next(1, 10));
}
private RedisValue GetRedisValueByKey(string key)
{
return RedisHelper.Get<RedisValue>(key);
}
}
1.StudentRedisService實現類
/// <summary>
/// Redis Student實現類
/// </summary>
public class StudentRedisService : AbstractRedisService<string, Student>,IStudentRedisService
{
protected override RedisGroup CacheGroup => RedisGroup.Student;
protected override string GetKeyByRedisInputKey(string redisInputKey) => redisInputKey;
protected override Student GetValueByKey(string redisInputKey)
{
return Test.AllStudents.Where(v=>v.Name == redisInputKey).FirstOrDefault();
}
}
2.使用StudentRedisService
private static void AbstractRedisServiceTest()
{
var studentRedisService = new StudentRedisService();
// 一:通過key獲取
var mark = studentRedisService.GetRedisByRedisInputKey("Mark"); // 有此數據,獲取的時候會寫入Redis
var linda = studentRedisService.GetRedisByRedisInputKey("Linda"); // 無數據則返回null
// 二:數據變更通知
string markName = "Mark";
// 更新數據
int random = new Random().Next();
Test.AllStudents.Where(v => v.Name == markName).First().Age = random;
var mark2 = studentRedisService.GetRedisByRedisInputKey(markName); // 舊值
studentRedisService.NoticeRedisUpdateByKey(markName);// 更新Redis
mark2 = studentRedisService.GetRedisByRedisInputKey(markName);// 新值
}
3.樣例源碼地址
調試Demo可以先參考Redis Runoob安裝教程,部署Redis服務,再進行調試
//github.com/Impartsoft/Bins/tree/main/CSRedisDemo/CSRedisDemo