1. 程式人生 > >Redis 應用場景

Redis 應用場景

快取

作為Key-value形態的記憶體資料庫,Redis最先會被想到的應用場景便是作為資料快取。而使用Redis快取資料非常簡單,只需要通過String型別將序列化後的物件存起來即可,不過也有一些需要注意的地方:

  • 必須保證不同物件的key不會重複,並且使key儘量短,一般使用類名(表名)加主鍵拼接。

  • 選擇一個優秀的序列化方式也很重要,目的是提高序列化的效率和減少記憶體佔用。

  • 快取內容與資料庫的一致性,這裡一般有兩種做法:

    • 只在資料庫查詢後將物件放入快取,如果物件發生了修改或刪除操作,直接清除對應快取(或設為過期)。

    • 在資料庫新增和查詢後將物件放入快取,修改後更新快取,刪除後清除對應快取(或設為過期)。

訊息佇列

Redis中list的資料結構實現的是雙向連結串列,所以可以非常便捷的應用於訊息佇列(生產者/消費者模型)。訊息的生產者只需要通過lpush將訊息放入list,消費者便可以通過rpop取出該訊息,並且可以保證訊息的有序性。如果需要實現帶有優先順序的訊息佇列也可以選擇sorted set。而pub/sub功能也可以用作釋出者/訂閱者模型的訊息。無論使用何種方式,由於Redis擁有持久化功能,也不需要擔心由於伺服器故障導致訊息丟失的情況。

時間軸

list作為雙向連結串列,不光可以作為佇列使用。如果將它用作棧便可以成為一個公用的時間軸。當用戶發完微博後,都通過lpush將它存放在一個 key 為LATEST_WEIBO的list中,之後便可以通過lrange取出當前最新的微博。

排行榜

使用sorted set和一個計算熱度的演算法便可以輕鬆打造一個熱度排行榜,zrevrangebyscore可以得到以分數倒序排列的序列,zrank可以得到一個成員在該排行榜的位置(是分數正序排列時的位置,如果要獲取倒序排列時的位置需要用zcard-zrank)。

計數器

計數功能應該是最適合 Redis 的使用場景之一了,因為它高頻率讀寫的特徵可以完全發揮 Redis 作為記憶體資料庫的高效。在 Redis 的資料結構中,string、hash和sorted set都提供了incr方法用於原子性的自增操作,下面舉例說明一下它們各自的使用場景:

  • 如果應用需要顯示每天的註冊使用者數,便可以使用string

    作為計數器,設定一個名為REGISTERED_COUNT_TODAY的 key,並在初始化時給它設定一個到凌晨 0 點的過期時間,每當使用者註冊成功後便使用incr命令使該 key 增長 1,同時當每天凌晨 0 點後,這個計數器都會因為 key 過期使值清零。

  • 每條微博都有點贊數、評論數、轉發數和瀏覽數四條屬性,這時用hash進行計數會更好,將該計數器的 key 設為weibo:weibo_idhash的 field 為like_numbercomment_numberforward_numberview_number,在對應操作後通過hincrby使hash 中的 field 自增。

  • 如果應用有一個發帖排行榜的功能,便選擇sorted set吧,將集合的 key 設為POST_RANK。當用戶發帖後,使用zincrby將該使用者 id 的 score 增長 1。sorted set會重新進行排序,使用者所在排行榜的位置也就會得到實時的更新。

好友關係

這個場景最開始是是一篇介紹微博 Redis 應用的 PPT 中看到的,其中提到微博的 Redis 主要是用在在計數和好友關係兩方面上,當時對好友關係方面的用法不太瞭解,後來看到《Redis 設計與實現》中介紹到作者最開始去使用 Redis 便是希望能通過set解決傳統資料庫無法快速計算集合中交集這個功能。後來聯想到微博當前的業務場景,確實能夠以這種方式實現,所以姑且猜測一下:

對於一個使用者 A,將它的關注和粉絲的使用者 id 都存放在兩個 set 中:

  • A:follow:存放 A 所有關注的使用者 id

  • A:follower:存放 A 所有粉絲的使用者 id

    那麼通過sinter命令便可以根據A:followA:follower的交集得到與 A 互相關注的使用者。當 A 進入另一個使用者 B 的主頁後,A:followB:follow的交集便是 A 和 B 的共同關注,A:followB:follower的交集便是 A 關注的人也關注了 B。

分散式鎖

在 Redis 2.6.12 版本開始,stringset命令增加了三個引數:

  • EX:設定鍵的過期時間(單位為秒)

  • PX:設定鍵的過期時間(單位為毫秒)

  • NX | XX:當設定為NX時,僅當 key 存在時才進行操作,設定為XX時,僅當 key 不存在才會進行操作

    由於這個操作是原子性的,可以簡單地以此實現一個分散式的鎖,例如:set key "lock" EX 1 XX

如果這個操作返回false,說明 key 的新增不成功,也就是當前有人在佔用這把鎖。而如果返回true,則說明得了鎖,便可以繼續進行操作,並且在操作後通過del命令釋放掉鎖。並且即使程式因為某些原因並沒有釋放鎖,由於設定了過期時間,該鎖也會在 1 秒後自動釋放,不會影響到其他程式的執行。

倒排索引

倒排索引是構造搜尋功能的最常見方式,在 Redis 中也可以通過set進行建立倒排索引,這裡以簡單的拼音 + 字首搜尋城市功能舉例:

假設一個城市北京,通過拼音詞庫將北京轉為beijing,再通過字首分詞將這兩個詞分為若干個字首索引,有:北京bbebeijinbeijing。將這些索引分別作為set的 key(例如:index:北)並存儲北京的 id,倒排索引便建立好了。接下來只需要在搜尋時通過關鍵詞取出對應的set並得到其中的 id 即可。

一些建議

  • Redis 速度快是建立在記憶體資料庫基礎上的,但是一臺伺服器的記憶體要比磁碟金貴許多,所以在專案初期不要想什麼都往 Redis 裡放,這樣當資料量上來後很快記憶體就會不夠用,反而得不償失。合理的利用有限的記憶體,將讀(寫)頻繁的熱資料放在 Redis 中才能更好感受到它帶來的效能提升。

  • Redis 雖然提供了RDBAOF兩種持久化方式,但是普遍還是認為 Redis 的持久化並不是很靠譜。這也是我一直不敢嘗試徹底的用 Redis 去實現第五點(好友關係)的原因。

  • 雖然 Redis 在 3.0 之後才推出官方的叢集方案,但是也有很多不錯的開源方案,比如Codis