【資料庫】Redis基礎篇

xbmchina發表於2019-04-28

歡迎關注公眾號:【愛編碼】 如果有需要後臺回覆2019贈送1T的學習資料哦!!

【資料庫】Redis基礎篇

簡介

Redis是一個開源(BSD許可)的記憶體資料結構儲存,用作資料庫、快取和訊息代理。它支援諸如字串、雜湊、列表、集、帶範圍查詢的排序集、點陣圖、hyperloglog、帶半徑查詢和流的地理空間索引等資料結構。

Redis具有內建的複製、Lua指令碼、LRU清除、事務和不同級別的磁碟永續性,並通過Redis Sentinel和Redis叢集的自動分割槽提供高可用性。

原理與架構

Redis使用了單執行緒架構和I/O多路複用模型來實現高效能的記憶體資料庫服務。

單執行緒模型

【資料庫】Redis基礎篇

因為Redis是單執行緒來處理命令的,所以一條命令從客戶端達到服務端不會立刻被執行。所有命令都會進入一個佇列中,然後逐個被執行,因此不會產生併發問題

為什麼單執行緒還能這麼快

  • 1.純記憶體訪問,Redis將所有資料放在記憶體中,記憶體的響應時長大 約為100納秒,這是Redis達到每秒萬級別訪問的重要基礎。

  • 2.非阻塞I/O,Redis使用epoll作為I/O多路複用技術的實現,再加上Redis自身的事件處理模型將epoll中的連線、讀寫、關閉都轉換為事件,不在網路I/O上浪費過多的時間,如下圖所示

    【資料庫】Redis基礎篇

    1. 單執行緒避免了執行緒切換和競態產生的消耗。

注:阻塞的操作是會非常影響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中集中獲取。

【資料庫】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{
// 限速
}
複製程式碼

雜湊

關係型資料表記錄的兩條使用者資訊,使用者的屬性作為表的列,每條使用者資訊作為行。

【資料庫】Redis基礎篇

相比於使用字串序列化快取使用者資訊,雜湊型別變得更加直觀,並且在更新操作上會更加便捷。可以將每個使用者的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基礎篇

  • 訊息佇列 Redis的lpush+brpop命令組合即可實現阻塞佇列,生產者客戶端使用lrpush從列表左側插入元素,多個消費者客戶端使用brpop命令阻塞式的“搶”列表尾部的元素,多個客戶端保證了消費的負載均衡和高可用性。如圖所示:
    【資料庫】Redis基礎篇

口訣:

  • lpush+lpop=Stack(棧)
  • lpush+rpop=Queue(佇列)
  • lpsh+ltrim=Capped Collection(有限集合)
  • lpush+brpop=Message Queue(訊息佇列)

集合

集合型別比較典型的使用場景是標籤(tag)。例如一個使用者可能對娛樂、體育比較感興趣,另一個使用者可能對歷史、新聞比較感興趣,這些興趣點就是標籤。 有了這些資料就可以得到喜歡同一個標籤的人,以及使用者的共同喜好的標籤,這些資料對於使用者體驗以及增強使用者黏度比較重要。 例如一個電子商務的網站會對不同標籤的使用者做不同型別的推薦,比如對數碼產品比較感興趣的人,在各個頁面或者通過郵件的形式給他們推薦最新的數碼產品,通常會為網站帶來更多的利益。

相關命令時間複雜度表:

【資料庫】Redis基礎篇

標籤實現基本思路

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有相關資料哦。

【資料庫】Redis基礎篇

相關文章