Redis到底該如何利用?

来源:https://www.cnblogs.com/supersnowyao/archive/2018/01/01/8167850.html
-Advertisement-
Play Games

Redis是個好東西,經過上兩個星期的研究和實踐,目前正在項目里大規模的替換掉原來的本地記憶體cache。但是替換過程中卻發現,Redis這東西高端,大氣上檔次,似乎不是我想象里的使用方法。 在沒有深入Redis之前,在我的概念里,緩存,就是key-value。而使用方式肯定不需要改動多少代碼,一切都 ...


    Redis是個好東西,經過上兩個星期的研究和實踐,目前正在項目里大規模的替換掉原來的本地記憶體cache。但是替換過程中卻發現,Redis這東西高端,大氣上檔次,似乎不是我想象里的使用方法。

    在沒有深入Redis之前,在我的概念里,緩存,就是key-value。而使用方式肯定不需要改動多少代碼,一切都是Get/Set。但是實際用的時候卻發現,我錯了,不是所有的場景都是簡單的Get/Set。也不是所有的數據都適合key-Value,於是有了這個問題,Redis到底該如何使用?我問自己,也向園子里的朋友們求助,希望幫忙解答。當然,這篇文章拋磚引玉,先談談我這兩周的感悟。

使用場景

    在我的項目里,有一個提供給Autocomplete的功能,數據量大概在幾萬。這篇文章里我用姓名檢索的例子來說明,列表請戳來自Redis作者的Demo。

    在這樣的列表裡全是用戶名,例如我們的系統里有一個用戶對象:

1 public Class User
2 {
3      public string Id{get; set;}  
4      public string Name {get; set;}
5      ....
6      public string UserHead {get; set;}    
7 }

    系統里需要一個用戶的下拉列表,由於數據量大不能一次顯示完,於是就加上了一個AutoComplete功能。如果是不用Redis這樣的集中式緩存,直接緩存在本機記憶體里,那麼結構很簡單如下:

1 var users = new List<User>{...};//讀到一個用戶列表
2 MemoryCache.Set("capqueen:users", users);//放入記憶體
3 
4 //讀取
5 var users = MemoryCache.Get<List<User>>("capqueen:users");

    因為都是在記憶體里,所以直接存List就可以了,搜索的時候也可以直接的如下:

1 var findUsers = users.Where(user => user.Name.StartWith("A")).ToList();例如輸入的字元是 "A"

    相當簡單,完全不用考慮如何存儲,存儲的數據結構。但是換到了Redis這些集中式緩存服務之後,咱們再來思考,該如何存儲。

    方案一:類似記憶體式的緩存實現。

    本文里使用的Redis鏈接庫是StactkExchange.Redis,出自StackOverFlow的開源產品。

1 var db = redis.GetDataBase();//獲取0資料庫
2 
3 var usersJson = JsonConvert.SerializeObject(users)//序列化
4 
5 db.StringSet("capqueen:users", usersJson);//存儲
6 
7 var usersString = db.StringGet("capqueen:users");
8 var userList = JsonConvert.DeserializeObject<List<User>>(usersString);//反序列化

    上面的方式邏輯上是沒有問題的,編譯也可以通過。但是仔細想一想,Redis作為獨立的緩存服務和appSever是分開來的,這樣的讀取方式對redis伺服器的IO是個負擔,甚至這樣的讀取比本地記憶體緩存慢了太多了。

    那如何解決呢?試想key-value的精髓是在於Key,那麼對於List來說應該要把item分開來存儲。

    方案二:Keys模糊匹配。

    在查看了Redis的命令文檔(見參考資料4)之後,發現了命令Keys,如獲至寶,立馬修改了方案。首先我們需要把要搜索的關鍵詞建立為key,這裡我把key定義為 "capqueen:user:{id}:{name}",其中{}內的是要用item對應屬性替換的。代碼如下:

 1 var redis = ConnectionMultiplexer.Connect("localhost");
 2 var db = redis.GetDatabase();
 3            
 4 var users = new List<User> { 
 5     new User{Id = 6, Name = "aaren", Age=10},
 6     new User{Id = 7, Name = "issy", Age=11},
 7     new User{Id = 8, Name = "janina", Age=13},
 8     new User{Id = 9, Name = "karena", Age=14}
 9 };
10 
11 users.ForEach(item => { 
12    var key = string.Format("capqueen:user:{0}:{1}", item.Id, item.Name);
13    var value = JsonConvert.SerializeObject(item);
14    db.StringSet(key, value);
15 });

    建立好的緩存如下圖所示:

    所有的user都以單獨的Key-Value方式存儲,那麼如何利用Keys搜索呢?我們來看下Redis的Keys命令:

1 KEYS pattern
2 
3 查找所有符合給定模式 pattern 的 key 。
4 
5 KEYS * 匹配資料庫中所有 key 。
6 KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
7 KEYS h*llo 匹配 hllo 和 heeeeello 等。
8 KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo 。
9 特殊符號用 \ 隔開

 

     也就是說Keys能夠進行簡單的模糊匹配,那麼我們這裡的搜索就可以換成如下的方式:

var redis = ConnectionMultiplexer.Connect("192.168.10.178");
var db = redis.GetDatabase();
var server = redis.GetServer("192.168.10.178", 6379);
var keys = server.Keys(pattern: "capqueen:user:*:a*");
var values = db.StringGet(keys.ToArray());

//反序列化
var jsonValues = new StringBuilder("[");
values.ToList().ForEach(item => jsonValues.Append(item).Append(","));
jsonValues.Append("]");
var userList = JsonConvert.DeserializeObject<List<User>>(jsonValues.ToString());

 

    註意以上的代碼里,因為每個value是一個json,為了增加轉化時的效率,我先處理成json arry再進行反序列化。

    這種方案,確實是解決了我目前的問題,然而我註意到了Redis文檔里的一段話:

    KEYS 的速度非常快,但在一個大的資料庫中使用它仍然可能造成性能問題,如果你需要從一個數據集中查找特定的 key ,你最好還是用 Redis 的集合結構(set)來代替。

    這段話換而言之就是慎用Keys搜索的意思,那麼有什麼更好的解決方案呢?由於這篇文章我拖得很久了,這裡留個問題在末尾,期待有大牛能夠幫忙解答,感激不盡。當然還有下一篇內容,我會講講我目前的處理方法。

下篇文章里,我會根據Redis作者的博客(資料1)里的做法以及我最後查到的資料,做一個新的方案,請大家到時指教。

參考資料

  1. Redis作者博客,這是其中一篇講如何基於Redis實現AutoComplete的文章:http://oldblog.antirez.com/post/autocomplete-with-redis.html
  2. Redis 第三方管理工具 For Windows:http://redisdesktop.com/
  3. Redis .NET鏈接工具的Top20:http://nugetmusthaves.com/Tag/Redis
  4. Redis命令中文文檔:http://redisdoc.com/

文章轉自:http://www.cnblogs.com/capqueen/p/HowToUseRedis.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 之前寫完vue項目後,佈置到伺服器,用nginx反向代理後,一開始進去,進各種路由都是沒問題的,但是一旦f5刷新後就會出現一個nginx404的錯誤。 經過翻閱vue文檔後,發現這是vueHistory 模式下的一個問題,需要後臺配置支持。 History 模式是沒有hash鍵,比如/a/b,f5刷 ...
  • 前言 這裡分享一個博主寫的省市區三級菜單聯動插件 — jQuery Citys,此插件中所有省市區數據均為國家行政區劃代碼,保證數據真實可靠,插件可以根據預設地區代碼或地區名稱進行值的初始化操作。 線上演示地址:https://yangyunhe369.github.io/jQuery Citys ...
  • 無限滾動載入最佳實踐 無限滾動(Infinite scrolling),有時候被稱為無盡滾動(endless scrolling),這種技術允許用戶在大量內容上滾動,眼中看不到結束的地方。這種技術很簡單,就是頁面往下滾動的時候保持刷新。 這項技術使用戶在沒有 打斷 和 額外交互 的情況下滾動列表 — ...
  • Promise API 簡介 譯者註: 到處是回調函數,代碼非常臃腫難看, Promise 主要用來解決這種編程方式, 將某些代碼封裝於內部。 Promise 直譯為“承諾”,但一般直接稱為 Promise; 代碼的可讀性非常重要,因為開發人員支出一般比電腦硬體的支出要大很多倍。 雖然同步代碼更容 ...
  • 在HTML5的規範中,我們可以通過為元素增加`draggable="true"`來設置此元素是否可以進行拖拽操作,其中圖片、鏈接預設是開啟的。 1. 拖拽元素:設置了`draggable="true"`的元素 當拖動某元素時,將依次觸發下列事件: 1. dragstart(按下滑鼠並開始移動滑鼠時, ...
  • 13.Label的作用是什麼?是怎麼用的? label標簽來定義表單控制間的關係,當用戶選擇該標簽時,瀏覽器會自動將焦點轉到和標簽相關的表單事件上。 <label for="Name">Number:</label> <input type="text" name="Name" id="Name"/ ...
  • 開篇 天天逛博客園,就是狠不下心來寫篇博客,忙是一方面,但是說忙能有多忙呢,都有時間逛博客園,寫篇博客的時間都沒有?(這還真不好說) 每次想到寫一篇新的設計模式,我總會問自己: 1,自己理解了嗎? 2,我能以一種簡單且有邏輯的方式把它說出來嗎? 不說做到有的放矢,但是一本正經的胡說八道還是要有吧,起 ...
  • Cache緩存在電腦領域是一個被普遍使用的概念。硬體中CPU有一級緩存,二級緩存, 瀏覽器中有緩存,軟體開發中也有分散式緩存memcache, redis。緩存無處不在的原因是它能夠極大地提高硬體和軟體的運行速度。在項目開發中,性能慢的地方常常是IO操作頻繁的地方,讀取資料庫是我們常見的消耗性能的 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...