Redis入門 - 數據類型:Stream詳解

Redis5.0 中還增加了一個數據類型Stream,它借鑑了Kafka的設計,是一個新的強大的支持多播的可持久化的消息隊列。@pdai

爲什麼會設計Stream

Redis5.0 中還增加了一個數據結構Stream,從字面上看是流類型,但其實從功能上看,應該是Redis對消息隊列(MQ,Message Queue)的完善實現。

用過Redis做消息隊列的都瞭解,基於Reids的消息隊列實現有很多種,例如:

  • PUB/SUB,訂閱/發佈模式
    • 但是發佈訂閱模式是無法持久化的,如果出現網絡斷開、Redis 宕機等,消息就會被丟棄;
  • 基於List LPUSH+BRPOP 或者 基於Sorted-Set的實現
    • 支持了持久化,但是不支持多播,分組消費等

爲什麼上面的結構無法滿足廣泛的MQ場景? 這裏便引出一個核心的問題:如果我們期望設計一種數據結構來實現消息隊列,最重要的就是要理解設計一個消息隊列需要考慮什麼?初步的我們很容易想到

  • 消息的生產
  • 消息的消費
    • 單播和多播(多對多)
    • 阻塞和非阻塞讀取
  • 消息有序性
  • 消息的持久化

其它還要考慮啥嗯?藉助美團技術團隊的一篇文章,消息隊列設計精要 中的圖

我們不妨看看Redis考慮了哪些設計

  • 消息ID的序列化生成
  • 消息遍歷
  • 消息的阻塞和非阻塞讀取
  • 消息的分組消費
  • 未完成消息的處理
  • 消息隊列監控
  • ...

這也是我們需要理解Stream的點,但是結合上面的圖,我們也應該理解Redis Stream也是一種超輕量MQ並沒有完全實現消息隊列所有設計要點,這決定着它適用的場景。

Stream詳解

經過梳理總結,我認爲從以下幾個大的方面去理解Stream是比較合適的,總結如下:@pdai

  • Stream的結構設計
  • 生產和消費
    • 基本的增刪查改
    • 單一消費者的消費
    • 消費組的消費
  • 監控狀態

Stream的結構

每個 Stream 都有唯一的名稱,它就是 Redis 的 key,在我們首次使用 xadd 指令追加消息時自動創建。

上圖解析:

  • Consumer Group :消費組,使用 XGROUP CREATE 命令創建,一個消費組有多個消費者(Consumer), 這些消費者之間是競爭關係。
  • last_delivered_id :遊標,每個消費組會有個遊標 last_delivered_id,任意一個消費者讀取了消息都會使遊標 last_delivered_id 往前移動。
  • pending_ids :消費者(Consumer)的狀態變量,作用是維護消費者的未確認的 id。 pending_ids 記錄了當前已經被客戶端讀取的消息,但是還沒有 ack (Acknowledge character:確認字符)。如果客戶端沒有ack,這個變量裏面的消息ID會越來越多,一旦某個消息被ack,它就開始減少。這個pending_ids變量在Redis官方被稱之爲PEL,也就是Pending Entries List,這是一個很核心的數據結構,它用來確保客戶端至少消費了消息一次,而不會在網絡傳輸的中途丟失了沒處理。

此外我們還需要理解兩點:

  • 消息ID: 消息ID的形式是timestampInMillis-sequence,例如1527846880572-5,它表示當前的消息在毫米時間戳1527846880572時產生,並且是該毫秒內產生的第5條消息。消息ID可以由服務器自動生成,也可以由客戶端自己指定,但是形式必須是整數-整數,而且必須是後面加入的消息的ID要大於前面的消息ID。
  • 消息內容: 消息內容就是鍵值對,形如hash結構的鍵值對,這沒什麼特別之處。

增刪改查

消息隊列相關命令:

  • XADD - 添加消息到末尾
  • XTRIM - 對流進行修剪,限制長度
  • XDEL - 刪除消息
  • XLEN - 獲取流包含的元素數量,即消息長度
  • XRANGE - 獲取消息列表,會自動過濾已經刪除的消息
  • XREVRANGE - 反向獲取消息列表,ID 從大到小
  • XREAD - 以阻塞或非阻塞方式獲取消息列表
# *號表示服務器自動生成ID,後面順序跟着一堆key/value
127.0.0.1:6379> xadd codehole * name laoqian age 30  #  名字叫laoqian,年齡30歲
1527849609889-0  # 生成的消息ID
127.0.0.1:6379> xadd codehole * name xiaoyu age 29
1527849629172-0
127.0.0.1:6379> xadd codehole * name xiaoqian age 1
1527849637634-0
127.0.0.1:6379> xlen codehole
(integer) 3
127.0.0.1:6379> xrange codehole - +  # -表示最小值, +表示最大值
127.0.0.1:6379> xrange codehole - +
1) 1) 1527849609889-0
   1) 1) "name"
      1) "laoqian"
      2) "age"
      3) "30"
2) 1) 1527849629172-0
   1) 1) "name"
      1) "xiaoyu"
      2) "age"
      3) "29"
3) 1) 1527849637634-0
   1) 1) "name"
      1) "xiaoqian"
      2) "age"
      3) "1"
127.0.0.1:6379> xrange codehole 1527849629172-0 +  # 指定最小消息ID的列表
1) 1) 1527849629172-0
   2) 1) "name"
      2) "xiaoyu"
      3) "age"
      4) "29"
2) 1) 1527849637634-0
   2) 1) "name"
      2) "xiaoqian"
      3) "age"
      4) "1"
127.0.0.1:6379> xrange codehole - 1527849629172-0  # 指定最大消息ID的列表
1) 1) 1527849609889-0
   2) 1) "name"
      2) "laoqian"
      3) "age"
      4) "30"
2) 1) 1527849629172-0
   2) 1) "name"
      2) "xiaoyu"
      3) "age"
      4) "29"
127.0.0.1:6379> xdel codehole 1527849609889-0
(integer) 1
127.0.0.1:6379> xlen codehole  # 長度不受影響
(integer) 3
127.0.0.1:6379> xrange codehole - +  # 被刪除的消息沒了
1) 1) 1527849629172-0
   2) 1) "name"
      2) "xiaoyu"
      3) "age"
      4) "29"
2) 1) 1527849637634-0
   2) 1) "name"
      2) "xiaoqian"
      3) "age"
      4) "1"
127.0.0.1:6379> del codehole  # 刪除整個Stream
(integer) 1

獨立消費

我們可以在不定義消費組的情況下進行Stream消息的獨立消費,當Stream沒有新消息時,甚至可以阻塞等待。Redis設計了一個單獨的消費指令xread,可以將Stream當成普通的消息隊列(list)來使用。使用xread時,我們可以完全忽略消費組(Consumer Group)的存在,就好比Stream就是一個普通的列表(list)。

# 從Stream頭部讀取兩條消息
127.0.0.1:6379> xread count 2 streams codehole 0-0
1) 1) "codehole"
   2) 1) 1) 1527851486781-0
         2) 1) "name"
            2) "laoqian"
            3) "age"
            4) "30"
      2) 1) 1527851493405-0
         2) 1) "name"
            2) "yurui"
            3) "age"
            4) "29"
# 從Stream尾部讀取一條消息,毫無疑問,這裏不會返回任何消息
127.0.0.1:6379> xread count 1 streams codehole $
(nil)
# 從尾部阻塞等待新消息到來,下面的指令會堵住,直到新消息到來
127.0.0.1:6379> xread block 0 count 1 streams codehole $
# 我們從新打開一個窗口,在這個窗口往Stream裏塞消息
127.0.0.1:6379> xadd codehole * name youming age 60
1527852774092-0
# 再切換到前面的窗口,我們可以看到阻塞解除了,返回了新的消息內容
# 而且還顯示了一個等待時間,這裏我們等待了93s
127.0.0.1:6379> xread block 0 count 1 streams codehole $
1) 1) "codehole"
   2) 1) 1) 1527852774092-0
         2) 1) "name"
            2) "youming"
            3) "age"
            4) "60"
(93.11s)

客戶端如果想要使用xread進行順序消費,一定要記住當前消費到哪裏了,也就是返回的消息ID。下次繼續調用xread時,將上次返回的最後一個消息ID作爲參數傳遞進去,就可以繼續消費後續的消息。

block 0表示永遠阻塞,直到消息到來,block 1000表示阻塞1s,如果1s內沒有任何消息到來,就返回nil

127.0.0.1:6379> xread block 1000 count 1 streams codehole $
(nil)
(1.07s)

消費組消費

  • 消費組消費圖

  • 相關命令:

    • XGROUP CREATE - 創建消費者組
    • XREADGROUP GROUP - 讀取消費者組中的消息
    • XACK - 將消息標記爲"已處理"
    • XGROUP SETID - 爲消費者組設置新的最後遞送消息ID
    • XGROUP DELCONSUMER - 刪除消費者
    • XGROUP DESTROY - 刪除消費者組
    • XPENDING - 顯示待處理消息的相關信息
    • XCLAIM - 轉移消息的歸屬權
    • XINFO - 查看流和消費者組的相關信息;
    • XINFO GROUPS - 打印消費者組的信息;
    • XINFO STREAM - 打印流信息
  • 創建消費組

Stream通過xgroup create指令創建消費組(Consumer Group),需要傳遞起始消息ID參數用來初始化last_delivered_id變量。

127.0.0.1:6379> xgroup create codehole cg1 0-0  #  表示從頭開始消費
OK
# $表示從尾部開始消費,只接受新消息,當前Stream消息會全部忽略
127.0.0.1:6379> xgroup create codehole cg2 $
OK
127.0.0.1:6379> xinfo stream codehole  # 獲取Stream信息
 1) length
 2) (integer) 3  # 共3個消息
 3) radix-tree-keys
 4) (integer) 1
 5) radix-tree-nodes
 6) (integer) 2
 7) groups
 8) (integer) 2  # 兩個消費組
 9) first-entry  # 第一個消息
10) 1) 1527851486781-0
    2) 1) "name"
       2) "laoqian"
       3) "age"
       4) "30"
11) last-entry  # 最後一個消息
12) 1) 1527851498956-0
    2) 1) "name"
       2) "xiaoqian"
       3) "age"
       4) "1"
127.0.0.1:6379> xinfo groups codehole  # 獲取Stream的消費組信息
1) 1) name
   2) "cg1"
   3) consumers
   4) (integer) 0  # 該消費組還沒有消費者
   5) pending
   6) (integer) 0  # 該消費組沒有正在處理的消息
2) 1) name
   2) "cg2"
   3) consumers  # 該消費組還沒有消費者
   4) (integer) 0
   5) pending
   6) (integer) 0  # 該消費組沒有正在處理的消息
  • 消費組消費

Stream提供了xreadgroup指令可以進行消費組的組內消費,需要提供消費組名稱、消費者名稱和起始消息ID。它同xread一樣,也可以阻塞等待新消息。讀到新消息後,對應的消息ID就會進入消費者的PEL(正在處理的消息)結構裏,客戶端處理完畢後使用xack指令通知服務器,本條消息已經處理完畢,該消息ID就會從PEL中移除。

# >號表示從當前消費組的last_delivered_id後面開始讀
# 每當消費者讀取一條消息,last_delivered_id變量就會前進
127.0.0.1:6379> xreadgroup GROUP cg1 c1 count 1 streams codehole >
1) 1) "codehole"
   2) 1) 1) 1527851486781-0
         2) 1) "name"
            2) "laoqian"
            3) "age"
            4) "30"
127.0.0.1:6379> xreadgroup GROUP cg1 c1 count 1 streams codehole >
1) 1) "codehole"
   2) 1) 1) 1527851493405-0
         2) 1) "name"
            2) "yurui"
            3) "age"
            4) "29"
127.0.0.1:6379> xreadgroup GROUP cg1 c1 count 2 streams codehole >
1) 1) "codehole"
   2) 1) 1) 1527851498956-0
         2) 1) "name"
            2) "xiaoqian"
            3) "age"
            4) "1"
      2) 1) 1527852774092-0
         2) 1) "name"
            2) "youming"
            3) "age"
            4) "60"
# 再繼續讀取,就沒有新消息了
127.0.0.1:6379> xreadgroup GROUP cg1 c1 count 1 streams codehole >
(nil)
# 那就阻塞等待吧
127.0.0.1:6379> xreadgroup GROUP cg1 c1 block 0 count 1 streams codehole >
# 開啓另一個窗口,往裏塞消息
127.0.0.1:6379> xadd codehole * name lanying age 61
1527854062442-0
# 回到前一個窗口,發現阻塞解除,收到新消息了
127.0.0.1:6379> xreadgroup GROUP cg1 c1 block 0 count 1 streams codehole >
1) 1) "codehole"
   2) 1) 1) 1527854062442-0
         2) 1) "name"
            2) "lanying"
            3) "age"
            4) "61"
(36.54s)
127.0.0.1:6379> xinfo groups codehole  # 觀察消費組信息
1) 1) name
   2) "cg1"
   3) consumers
   4) (integer) 1  # 一個消費者
   5) pending
   6) (integer) 5  # 共5條正在處理的信息還有沒有ack
2) 1) name
   2) "cg2"
   3) consumers
   4) (integer) 0  # 消費組cg2沒有任何變化,因爲前面我們一直在操縱cg1
   5) pending
   6) (integer) 0
# 如果同一個消費組有多個消費者,我們可以通過xinfo consumers指令觀察每個消費者的狀態
127.0.0.1:6379> xinfo consumers codehole cg1  # 目前還有1個消費者
1) 1) name
   2) "c1"
   3) pending
   4) (integer) 5  # 共5條待處理消息
   5) idle
   6) (integer) 418715  # 空閒了多長時間ms沒有讀取消息了
# 接下來我們ack一條消息
127.0.0.1:6379> xack codehole cg1 1527851486781-0
(integer) 1
127.0.0.1:6379> xinfo consumers codehole cg1
1) 1) name
   2) "c1"
   3) pending
   4) (integer) 4  # 變成了5條
   5) idle
   6) (integer) 668504
# 下面ack所有消息
127.0.0.1:6379> xack codehole cg1 1527851493405-0 1527851498956-0 1527852774092-0 1527854062442-0
(integer) 4
127.0.0.1:6379> xinfo consumers codehole cg1
1) 1) name
   2) "c1"
   3) pending
   4) (integer) 0  # pel空了
   5) idle
   6) (integer) 745505

信息監控

Stream提供了XINFO來實現對服務器信息的監控,可以查詢:

  • 查看隊列信息
127.0.0.1:6379> Xinfo stream mq
 1) "length"
 2) (integer) 7
 3) "radix-tree-keys"
 4) (integer) 1
 5) "radix-tree-nodes"
 6) (integer) 2
 7) "groups"
 8) (integer) 1
 9) "last-generated-id"
10) "1553585533795-9"
11) "first-entry"
12) 1) "1553585533795-3"
    2) 1) "msg"
       2) "4"
13) "last-entry"
14) 1) "1553585533795-9"
    2) 1) "msg"
       2) "10"
  • 消費組信息
127.0.0.1:6379> Xinfo groups mq
1) 1) "name"
   2) "mqGroup"
   3) "consumers"
   4) (integer) 3
   5) "pending"
   6) (integer) 3
   7) "last-delivered-id"
   8) "1553585533795-4"
  • 消費者組成員信息
127.0.0.1:6379> XINFO CONSUMERS mq mqGroup
1) 1) "name"
   2) "consumerA"
   3) "pending"
   4) (integer) 1
   5) "idle"
   6) (integer) 18949894
2) 1) "name"
   2) "consumerB"
   3) "pending"
   4) (integer) 1
   5) "idle"
   6) (integer) 3092719
3) 1) "name"
   2) "consumerC"
   3) "pending"
   4) (integer) 1
   5) "idle"
   6) (integer) 23683256

至此,消息隊列的操作說明大體結束!

更深入理解

我們結合MQ中常見問題,看Redis是如何解決的,來進一步理解Redis。

Stream用在什麼樣場景

可用作時通信等,大數據分析,異地數據備份等

客戶端可以平滑擴展,提高處理能力

消息ID的設計是否考慮了時間回撥的問題?

分佈式算法 - ID算法設計中, 一個常見的問題就是時間回撥問題,那麼Redis的消息ID設計中是否考慮到這個問題呢?

XADD生成的1553439850328-0,就是Redis生成的消息ID,由兩部分組成:時間戳-序號。時間戳是毫秒級單位,是生成消息的Redis服務器時間,它是個64位整型(int64)。序號是在這個毫秒時間點內的消息序號,它也是個64位整型。

可以通過multi批處理,來驗證序號的遞增:

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> XADD memberMessage * msg one
QUEUED
127.0.0.1:6379> XADD memberMessage * msg two
QUEUED
127.0.0.1:6379> XADD memberMessage * msg three
QUEUED
127.0.0.1:6379> XADD memberMessage * msg four
QUEUED
127.0.0.1:6379> XADD memberMessage * msg five
QUEUED
127.0.0.1:6379> EXEC
1) "1553441006884-0"
2) "1553441006884-1"
3) "1553441006884-2"
4) "1553441006884-3"
5) "1553441006884-4"

由於一個redis命令的執行很快,所以可以看到在同一時間戳內,是通過序號遞增來表示消息的。

爲了保證消息是有序的,因此Redis生成的ID是單調遞增有序的。由於ID中包含時間戳部分,爲了避免服務器時間錯誤而帶來的問題(例如服務器時間延後了),Redis的每個Stream類型數據都維護一個latest_generated_id屬性,用於記錄最後一個消息的ID。若發現當前時間戳退後(小於latest_generated_id所記錄的),則採用時間戳不變而序號遞增的方案來作爲新消息ID(這也是序號爲什麼使用int64的原因,保證有足夠多的的序號),從而保證ID的單調遞增性質。

強烈建議使用Redis的方案生成消息ID,因爲這種時間戳+序號的單調遞增的ID方案,幾乎可以滿足你全部的需求。但同時,記住ID是支持自定義的,別忘了!

消費者崩潰帶來的會不會消息丟失問題?

爲了解決組內消息讀取但處理期間消費者崩潰帶來的消息丟失問題,STREAM 設計了 Pending 列表,用於記錄讀取但並未處理完畢的消息。命令XPENDIING 用來獲消費組或消費內消費者的未處理完畢的消息。演示如下:

127.0.0.1:6379> XPENDING mq mqGroup # mpGroup的Pending情況
1) (integer) 5 # 5個已讀取但未處理的消息
2) "1553585533795-0" # 起始ID
3) "1553585533795-4" # 結束ID
4) 1) 1) "consumerA" # 消費者A有3個
      2) "3"
   2) 1) "consumerB" # 消費者B有1個
      2) "1"
   3) 1) "consumerC" # 消費者C有1個
      2) "1"

127.0.0.1:6379> XPENDING mq mqGroup - + 10 # 使用 start end count 選項可以獲取詳細信息
1) 1) "1553585533795-0" # 消息ID
   2) "consumerA" # 消費者
   3) (integer) 1654355 # 從讀取到現在經歷了1654355ms,IDLE
   4) (integer) 5 # 消息被讀取了5次,delivery counter
2) 1) "1553585533795-1"
   2) "consumerA"
   3) (integer) 1654355
   4) (integer) 4
# 共5個,餘下3個省略 ...

127.0.0.1:6379> XPENDING mq mqGroup - + 10 consumerA # 在加上消費者參數,獲取具體某個消費者的Pending列表
1) 1) "1553585533795-0"
   2) "consumerA"
   3) (integer) 1641083
   4) (integer) 5
# 共3個,餘下2個省略 ...

每個Pending的消息有4個屬性:

  • 消息ID
  • 所屬消費者
  • IDLE,已讀取時長
  • delivery counter,消息被讀取次數

上面的結果我們可以看到,我們之前讀取的消息,都被記錄在Pending列表中,說明全部讀到的消息都沒有處理,僅僅是讀取了。那如何表示消費者處理完畢了消息呢?使用命令 XACK 完成告知消息處理完成,演示如下:

127.0.0.1:6379> XACK mq mqGroup 1553585533795-0 # 通知消息處理結束,用消息ID標識
(integer) 1

127.0.0.1:6379> XPENDING mq mqGroup # 再次查看Pending列表
1) (integer) 4 # 已讀取但未處理的消息已經變爲4個
2) "1553585533795-1"
3) "1553585533795-4"
4) 1) 1) "consumerA" # 消費者A,還有2個消息處理
      2) "2"
   2) 1) "consumerB"
      2) "1"
   3) 1) "consumerC"
      2) "1"
127.0.0.1:6379>

有了這樣一個Pending機制,就意味着在某個消費者讀取消息但未處理後,消息是不會丟失的。等待消費者再次上線後,可以讀取該Pending列表,就可以繼續處理該消息了,保證消息的有序和不丟失。

消費者徹底宕機後如何轉移給其它消費者處理?

還有一個問題,就是若某個消費者宕機之後,沒有辦法再上線了,那麼就需要將該消費者Pending的消息,轉義給其他的消費者處理,就是消息轉移。

消息轉移的操作時將某個消息轉移到自己的Pending列表中。使用語法XCLAIM來實現,需要設置組、轉移的目標消費者和消息ID,同時需要提供IDLE(已被讀取時長),只有超過這個時長,才能被轉移。演示如下:

# 當前屬於消費者A的消息1553585533795-1,已經15907,787ms未處理了
127.0.0.1:6379> XPENDING mq mqGroup - + 10
1) 1) "1553585533795-1"
   2) "consumerA"
   3) (integer) 15907787
   4) (integer) 4

# 轉移超過3600s的消息1553585533795-1到消費者B的Pending列表
127.0.0.1:6379> XCLAIM mq mqGroup consumerB 3600000 1553585533795-1
1) 1) "1553585533795-1"
   2) 1) "msg"
      2) "2"

# 消息1553585533795-1已經轉移到消費者B的Pending中。
127.0.0.1:6379> XPENDING mq mqGroup - + 10
1) 1) "1553585533795-1"
   2) "consumerB"
   3) (integer) 84404 # 注意IDLE,被重置了
   4) (integer) 5 # 注意,讀取次數也累加了1次

以上代碼,完成了一次消息轉移。轉移除了要指定ID外,還需要指定IDLE,保證是長時間未處理的才被轉移。被轉移的消息的IDLE會被重置,用以保證不會被重複轉移,以爲可能會出現將過期的消息同時轉移給多個消費者的併發操作,設置了IDLE,則可以避免後面的轉移不會成功,因爲IDLE不滿足條件。例如下面的連續兩條轉移,第二條不會成功。

127.0.0.1:6379> XCLAIM mq mqGroup consumerB 3600000 1553585533795-1
127.0.0.1:6379> XCLAIM mq mqGroup consumerC 3600000 1553585533795-1

這就是消息轉移。至此我們使用了一個Pending消息的ID,所屬消費者和IDLE的屬性,還有一個屬性就是消息被讀取次數,delivery counter,該屬性的作用由於統計消息被讀取的次數,包括被轉移也算。這個屬性主要用在判定是否爲錯誤數據上。

壞消息問題,Dead Letter,死信問題

正如上面所說,如果某個消息,不能被消費者處理,也就是不能被XACK,這是要長時間處於Pending列表中,即使被反覆的轉移給各個消費者也是如此。此時該消息的delivery counter就會累加(上一節的例子可以看到),當累加到某個我們預設的臨界值時,我們就認爲是壞消息(也叫死信,DeadLetter,無法投遞的消息),由於有了判定條件,我們將壞消息處理掉即可,刪除即可。刪除一個消息,使用XDEL語法,演示如下:

# 刪除隊列中的消息
127.0.0.1:6379> XDEL mq 1553585533795-1
(integer) 1
# 查看隊列中再無此消息
127.0.0.1:6379> XRANGE mq - +
1) 1) "1553585533795-0"
   2) 1) "msg"
      2) "1"
2) 1) "1553585533795-2"
   2) 1) "msg"
      2) "3"

注意本例中,並沒有刪除Pending中的消息因此你查看Pending,消息還會在。可以執行XACK標識其處理完畢!

參考文章

本文主要梳理總結自:

知識體系

知識體系

相關文章

首先,我們通過學習Redis的概念基礎,瞭解它適用的場景。

  • Redis入門 - Redis概念和基礎
    • Redis是一種支持key-value等多種數據結構的存儲系統。可用於緩存,事件發佈或訂閱,高速隊列等場景。支持網絡,提供字符串,哈希,列表,隊列,集合結構直接存取,基於內存,可持久化。

其次,這些適用場景都是基於Redis支持的數據類型的,所以我們需要學習它支持的數據類型;同時在redis優化中還需要對底層數據結構瞭解,所以也需要了解一些底層數據結構的設計和實現。

再者,需要學習Redis支持的核心功能,包括持久化,消息,事務,高可用;高可用方面包括,主從,哨兵等;高可拓展方面,比如 分片機制等。

  • Redis進階 - 持久化:RDB和AOF機制詳解
    • 爲了防止數據丟失以及服務重啓時能夠恢復數據,Redis支持數據的持久化,主要分爲兩種方式,分別是RDB和AOF; 當然實際場景下還會使用這兩種的混合模式。
  • Redis進階 - 消息傳遞:發佈訂閱模式詳解
    • Redis 發佈訂閱(pub/sub)是一種消息通信模式:發送者(pub)發送消息,訂閱者(sub)接收消息。
  • Redis進階 - 事件:Redis事件機制詳解
    • Redis 採用事件驅動機制來處理大量的網絡IO。它並沒有使用 libevent 或者 libev 這樣的成熟開源方案,而是自己實現一個非常簡潔的事件驅動庫 ae_event。
  • Redis進階 - 事務:Redis事務詳解
    • Redis 事務的本質是一組命令的集合。事務支持一次執行多個命令,一個事務中所有命令都會被序列化。在事務執行過程,會按照順序串行化執行隊列中的命令,其他客戶端提交的命令請求不會插入到事務執行命令序列中。
  • Redis進階 - 高可用:主從複製詳解
    • 我們知道要避免單點故障,即保證高可用,便需要冗餘(副本)方式提供集羣服務。而Redis 提供了主從庫模式,以保證數據副本的一致,主從庫之間採用的是讀寫分離的方式。本文主要闡述Redis的主從複製。
  • Redis進階 - 高可用:哨兵機制(Redis Sentinel)詳解
    • 在上文主從複製的基礎上,如果注節點出現故障該怎麼辦呢? 在 Redis 主從集羣中,哨兵機制是實現主從庫自動切換的關鍵機制,它有效地解決了主從複製模式下故障轉移的問題。
  • Redis進階 - 高可拓展:分片技術(Redis Cluster)詳解
    • 前面兩篇文章,主從複製和哨兵機制保障了高可用,就讀寫分離而言雖然slave節點來擴展主從的讀併發能力,但是寫能力和存儲能力是無法進行擴展的,就只能是master節點能夠承載的上限。如果面對海量數據那麼必然需要構建master(主節點分片)之間的集羣,同時必然需要吸收高可用(主從複製和哨兵機制)能力,即每個master分片節點還需要有slave節點,這是分佈式系統中典型的縱向擴展(集羣的分片技術)的體現;所以在Redis 3.0版本中對應的設計就是Redis Cluster。

最後,就是具體的實踐以及實踐中遇到的問題和解決方法了:在不同版本中有不同特性,所以還需要了解版本;以及性能優化,大廠實踐等。

  • Redis進階 - 緩存問題:一致性, 穿擊, 穿透, 雪崩, 污染等
    • Redis最常用的一個場景就是作爲緩存,本文主要探討作爲緩存,在實踐中可能會有哪些問題?比如一致性, 穿擊, 穿透, 雪崩, 污染等
  • Redis進階 - 版本特性: Redis4.0、5.0、6.0特性整理
    • 在學習Redis知識體系時,我們難免會需要查看版本實現之間的差異,本文主要整理Redis較爲新的版本的特性。
  • Redis進階 - 運維監控:Redis的監控詳解
    • Redis實戰中包含開發,集羣 和 運維,Redis用的好不好,如何讓它更好,這是運維要做的;本文主要在 Redis自身狀態及命令可視化監控工具,以及Redis監控體系等方面幫助你構建對redis運維/監控體系的認知,它是性能優化的前提。
  • Redis進階 - 性能調優:Redis性能調優詳解
    • Redis 的性能問題,涉及到的知識點非常廣,幾乎涵蓋了 CPU、內存、網絡、甚至磁盤的方方面面;同時還需要對上文中一些基礎或底層有詳細的瞭解。針對Redis的性能調優,這裏整理分享一篇水滴與銀彈(公衆號)的文章,這篇文章可以幫助你構築Redis性能調優的知識體系。
  • Redis大廠經驗 - 微博:萬億級日訪問量下,Redis在微博的9年優化歷程
    • 再分享一篇微博使用redis的經驗的文章,因爲Redis在微博內部分佈在各個應用場景,比如像現在春晚必爭的“紅包飛”活動,還有像粉絲數、用戶數、閱讀數、轉評贊、評論蓋樓、廣告推薦、負反饋、音樂榜單等等都有用到Redis;我們可以通過大廠使用redis的經驗來強化對redis使用上的認知。

學習資料

本篇文章由一文多發平臺ArtiPub自動發佈

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章