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_id
,hash
的 field 為like_number
、comment_number
、forward_number
和view_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:follow
和A:follower
的交集得到與 A 互相關注的使用者。當 A 進入另一個使用者 B 的主頁後,A:follow
和B:follow
的交集便是 A 和 B 的共同關注,A:follow
和B:follower
的交集便是 A 關注的人也關注了 B。
分散式鎖
在 Redis 2.6.12 版本開始,string
的set
命令增加了三個引數:
-
EX
:設定鍵的過期時間(單位為秒) -
PX
:設定鍵的過期時間(單位為毫秒) -
NX
|XX
:當設定為NX
時,僅當 key 存在時才進行操作,設定為XX
時,僅當 key 不存在才會進行操作由於這個操作是原子性的,可以簡單地以此實現一個分散式的鎖,例如:
set key "lock" EX 1 XX
如果這個操作返回false
,說明 key 的新增不成功,也就是當前有人在佔用這把鎖。而如果返回true
,則說明得了鎖,便可以繼續進行操作,並且在操作後通過del
命令釋放掉鎖。並且即使程式因為某些原因並沒有釋放鎖,由於設定了過期時間,該鎖也會在 1 秒後自動釋放,不會影響到其他程式的執行。
倒排索引
倒排索引是構造搜尋功能的最常見方式,在 Redis 中也可以通過set
進行建立倒排索引,這裡以簡單的拼音 + 字首搜尋城市功能舉例:
假設一個城市北京
,通過拼音詞庫將北京
轉為beijing
,再通過字首分詞將這兩個詞分為若干個字首索引,有:北
、北京
、b
、be
…beijin
和beijing
。將這些索引分別作為set
的 key(例如:index:北
)並存儲北京
的 id,倒排索引便建立好了。接下來只需要在搜尋時通過關鍵詞取出對應的set
並得到其中的 id 即可。
一些建議
-
Redis 速度快是建立在記憶體資料庫基礎上的,但是一臺伺服器的記憶體要比磁碟金貴許多,所以在專案初期不要想什麼都往 Redis 裡放,這樣當資料量上來後很快記憶體就會不夠用,反而得不償失。合理的利用有限的記憶體,將讀(寫)頻繁的熱資料放在 Redis 中才能更好感受到它帶來的效能提升。
-
Redis 雖然提供了
RDB
和AOF
兩種持久化方式,但是普遍還是認為 Redis 的持久化並不是很靠譜。這也是我一直不敢嘗試徹底的用 Redis 去實現第五點(好友關係)的原因。
-
雖然 Redis 在 3.0 之後才推出官方的叢集方案,但是也有很多不錯的開源方案,比如
Codis
。