.NET Core or .NET Framework 4.0+ client for Redis and Redis Sentinel (2.8) and Cluster. Includes both synchronous and asynchronous clients.html
本文記錄CSRedis在開發過程當中的簡單使用,能夠直接調試樣例源碼。git
github https://github.com/2881099/csredisgithub
做者博客 http://www.javashuo.com/article/p-ykiugmwg-dy.htmlredis
Redis Runoob教程 https://www.runoob.com/redis/redis-install.html緩存
1.科學使用緩存併發
從Redis中讀取數據須要考慮"數據存在,可是Redis中過時或者未寫入的狀況"這時候就須要根據指定Key先獲取數據再寫入Redis中。dom
寫入Redis須要增長過時時。增長過時時間的時候能夠將時間隨機,這樣能夠避免緩存在相同時間過時而引起緩存雪崩。async
在高併發的狀況下,若是根據key獲取的數據不存在,也將null保存至Redis中,而非抽象類(AbstractRedisService)中作刪除動做,這樣可避免緩存穿透。ide
我對高併發場景下的緩存使用理解不深,AbstractRedisService抽象類在更新失效值的時候使用了lock,使用lock寫法容易形成堵塞,可是若是不使用lock的話,就會出現重複讀取再寫入Redis的狀況,而且在當前狀況下如何處理緩存擊穿也不是很清楚,但願你們能不吝賜教~高併發
2.在使用CSRedis的時候遇到了多個業務模塊都有相識的代碼,因而抽取了抽象類AbstractRedisService,在業務模塊實現的時候只須要實現RedisGroup 屬性與GetKeyByRedisInputKey、GetValueByKey兩個方法。
3.在實際應用中,有可能會出現程序與Redis服務鏈接不穩定的狀況,若是Redis服務沒有發現問題的話,能夠嘗試使用下面三種方式解決(參考 https://github.com/2881099/csredis/issues)
connectTimeout=30000 設置鏈接超時時間
tryit=3 設置重試次數
preheat=100 預熱鏈接數
// 初始化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); } }
/// <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); } }
/// <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(); } }
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);// 新值 }
調試Demo能夠先參考Redis Runoob安裝教程,部署Redis服務,再進行調試
https://github.com/Impartsoft/Bins/tree/main/CSRedisDemo/CSRedisDemo