歡迎關注公眾號:【愛編碼】 如果有需要後臺回覆2019贈送1T的學習資料哦!!
簡介
Redis是一個開源(BSD許可)的記憶體資料結構儲存,用作資料庫、快取和訊息代理。它支援諸如字串、雜湊、列表、集、帶範圍查詢的排序集、點陣圖、hyperloglog、帶半徑查詢和流的地理空間索引等資料結構。
Redis具有內建的複製、Lua指令碼、LRU清除、事務和不同級別的磁碟永續性,並通過Redis Sentinel和Redis叢集的自動分割槽提供高可用性。
原理與架構
Redis使用了單執行緒架構和I/O多路複用模型來實現高效能的記憶體資料庫服務。
單執行緒模型
因為Redis是單執行緒來處理命令的,所以一條命令從客戶端達到服務端不會立刻被執行。所有命令都會進入一個佇列中,然後逐個被執行,因此不會產生併發問題。
為什麼單執行緒還能這麼快
-
1.純記憶體訪問,Redis將所有資料放在記憶體中,記憶體的響應時長大 約為100納秒,這是Redis達到每秒萬級別訪問的重要基礎。
-
2.非阻塞I/O,Redis使用epoll作為I/O多路複用技術的實現,再加上Redis自身的事件處理模型將epoll中的連線、讀寫、關閉都轉換為事件,不在網路I/O上浪費過多的時間,如下圖所示
-
- 單執行緒避免了執行緒切換和競態產生的消耗。
注:阻塞的操作是會非常影響Redis效能,這個下次再總結
API使用場景
命令語法可以到下面地址查,本節僅僅說使用場景。 www.runoob.com/redis/redis…
字串
- 1.快取功能 Redis作為快取層,MySQL作為儲存層,絕大部分請求的資料都是從Redis中獲取。由於Redis具有支撐高併發的特性,所以快取通常能起到加速讀寫和降低後端壓力的作用。
類似下面這樣子的虛擬碼
// 從MySQL獲取使用者資訊
userInfo = mysql.get(id);
// 將userInfo序列化,並存入Redis
redis.setex(userRedisKey, 3600, serialize(userInfo));
// 返回結果
return userInfo
複製程式碼
- 2.計數 例如使用Redis作為文章點贊數計數的基礎元件,使用者每一次點贊,相應的點贊數就會自增1
long incrLikeCounter(long id) {
key = "article:like:" + id;
return redis.incr(key);
}
複製程式碼
- 3.共享Session 使用Redis將使用者的Session進行集中管理,每次使用者更新或者查詢登入資訊都直接從Redis中集中獲取。
- 4.限速 很多應用出於安全的考慮,會在每次進行登入時,讓使用者輸入手機驗證碼,從而確定是否是使用者本人。但是為了簡訊介面不被頻繁訪問,會限制使用者每分鐘獲取驗證碼的頻率。 類似如下虛擬碼
phoneNum = "138xxxxxxxx";
key = "shortMsg:limit:" + phoneNum;
// SET key value EX 60 NX
isExists = redis.set(key,1,"EX 60","NX");
if(isExists != null || redis.incr(key) <=5){
// 通過
}else{
// 限速
}
複製程式碼
雜湊
關係型資料表記錄的兩條使用者資訊,使用者的屬性作為表的列,每條使用者資訊作為行。
相比於使用字串序列化快取使用者資訊,雜湊型別變得更加直觀,並且在更新操作上會更加便捷。可以將每個使用者的id定義為鍵字尾,多對fieldvalue對應每個使用者的屬性。 類似如下虛擬碼:
UserInfo getUserInfo(long id){
// 使用者id作為key字尾
userRedisKey = "user:info:" + id;
// 使用hgetall獲取所有使用者資訊對映關係
userInfoMap = redis.hgetAll(userRedisKey);
UserInfo userInfo;
if (userInfoMap != null) {
// 將對映關係轉換為UserInfo
userInfo = transferMapToUserInfo(userInfoMap);
} else {
// 從MySQL中獲取使用者資訊
userInfo = mysql.get(id);
// 將userInfo變為對映關係使用hmset儲存到Redis中
redis.hmset(userRedisKey, transferUserInfoToMap(userInfo));
// 新增過期時間
redis.expire(userRedisKey, 3600);
}
return userInfo;
}
複製程式碼
列表
列表是一種比較靈活的資料結構,它可以充當棧和佇列。
相關命令時間複雜度表:
- 訊息佇列 Redis的lpush+brpop命令組合即可實現阻塞佇列,生產者客戶端使用lrpush從列表左側插入元素,多個消費者客戶端使用brpop命令阻塞式的“搶”列表尾部的元素,多個客戶端保證了消費的負載均衡和高可用性。如圖所示:
口訣:
- lpush+lpop=Stack(棧)
- lpush+rpop=Queue(佇列)
- lpsh+ltrim=Capped Collection(有限集合)
- lpush+brpop=Message Queue(訊息佇列)
集合
集合型別比較典型的使用場景是標籤(tag)。例如一個使用者可能對娛樂、體育比較感興趣,另一個使用者可能對歷史、新聞比較感興趣,這些興趣點就是標籤。 有了這些資料就可以得到喜歡同一個標籤的人,以及使用者的共同喜好的標籤,這些資料對於使用者體驗以及增強使用者黏度比較重要。 例如一個電子商務的網站會對不同標籤的使用者做不同型別的推薦,比如對數碼產品比較感興趣的人,在各個頁面或者通過郵件的形式給他們推薦最新的數碼產品,通常會為網站帶來更多的利益。
相關命令時間複雜度表:
標籤實現基本思路
1.給使用者新增標籤
sadd user:1:tags tag1 tag2 tag5
sadd user:2:tags tag2 tag3 tag5
...
sadd user:k:tags tag1 tag2 tag4
...
複製程式碼
2.給標籤新增使用者
sadd tag1:users user:1 user:3
sadd tag2:users user:1 user:2 user:3
...
sadd tagk:users user:1 user:2
...
複製程式碼
3.使用sinter命令,來計算使用者共同感興趣的標籤
sinter user:1:tags user:2:tags
複製程式碼
注:1,2步應該在同一個事務(下一篇文章再講)中執行,否則會導致資料不正確。
更多組合應用:
- sadd=Tagging(標籤)
- spop/srandmember=Random item(生成隨機數,比如抽獎)
- sadd+sinter=Social Graph(社交需求)
有序集合
它保留了集合不能有重複成員的特性,給每個元素設定一個**分數(score)**作為排序的依據。
場景
排行榜系統 例如視訊網站需要對使用者上傳的視訊做排行榜,榜單的維度可能是多個方面的:按照時間、按照播放數量、按照獲得的贊數。
本節使用贊數這個維度,記錄每天使用者上傳視訊的排行榜。 主要需要實現以下4個功能
- 1.新增使用者贊數
//獲得一個贊。
zadd user:ranking:2016_03_15 mike 3
//第一個贊之後自增。
zincrby user:ranking:2016_03_15 mike 1
複製程式碼
- 2.取消使用者贊數
zrem user:ranking:2016_03_15 mike
複製程式碼
- 3.展示獲取贊數最多的十個使用者
zrevrangebyrank user:ranking:2016_03_15 0 9
複製程式碼
- 4.展示使用者資訊以及使用者分數
hgetall user:info:tom
zscore user:ranking:2016_03_15 mike
zrank user:ranking:2016_03_15 mike
複製程式碼
總結
Redis還有什麼場景,歡迎各位大神指教。
本文所有知識點來自於【Redis開發與運維(付磊)】,這本書非常值得一讀。
關注公眾號【愛編碼】回覆付磊即可獲取。
最後
如果對 Java、大資料感興趣請長按二維碼關注一波,我會努力帶給你們價值。覺得對你哪怕有一丁點幫助的請幫忙點個贊或者轉發哦。
關注公眾號【愛編碼】,回覆2019有相關資料哦。