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標識其處理完畢!
參考文章
本文主要梳理總結自:
- https://www.runoob.com/redis/redis-stream.html
- https://www.zhihu.com/question/279540635
- https://zhuanlan.zhihu.com/p/60501638
知識體系
知識體系
相關文章
首先,我們通過學習Redis的概念基礎,瞭解它適用的場景。
- Redis入門 - Redis概念和基礎
- Redis是一種支持key-value等多種數據結構的存儲系統。可用於緩存,事件發佈或訂閱,高速隊列等場景。支持網絡,提供字符串,哈希,列表,隊列,集合結構直接存取,基於內存,可持久化。
其次,這些適用場景都是基於Redis支持的數據類型的,所以我們需要學習它支持的數據類型;同時在redis優化中還需要對底層數據結構瞭解,所以也需要了解一些底層數據結構的設計和實現。
- Redis入門 - 數據類型:5種基礎數據類型詳解
- Redis所有的key(鍵)都是字符串。我們在談基礎數據結構時,討論的是存儲值的數據類型,主要包括常見的5種數據類型,分別是:String、List、Set、Zset、Hash
- Redis入門 - 數據類型:3種特殊類型詳解
- Redis除了上文中5種基礎數據類型,還有三種特殊的數據類型,分別是 HyperLogLogs(基數統計), Bitmaps (位圖) 和 geospatial (地理位置)
- Redis入門 - 數據類型:Stream詳解
- Redis5.0 中還增加了一個數據結構Stream,它借鑑了Kafka的設計,是一個新的強大的支持多播的可持久化的消息隊列。
- Redis進階 - 底層數據結構:對象機制詳解
- 我們在前文已經闡述了Redis 5種基礎數據類型詳解,分別是字符串(string)、列表(list)、哈希(hash)、集合(set)、有序集合(zset),以及5.0版本中Redis Stream結構詳解;那麼這些基礎類型的底層是如何實現的呢?Redis的每種對象其實都由對象結構(redisObject) 與 對應編碼的數據結構組合而成, 本文主要介紹對象結構(redisObject) 部分。。
- Redis進階 - 底層數據結構:底層數據結構詳解
- 前文是第一部分底層設計:對象機制詳解, 本文主要介紹底層數據結構 部分。
- Redis進階 - 底層數據結構: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使用上的認知。
學習資料
-
Redis官網:http://redis.io/
-
Redis官方文檔:http://redis.io/documentation
-
Redis下載:http://redis.io/download
-
redis英文文檔 https://redis.io/topics/data-types
-
redis中文文檔 http://www.redis.cn/documentation.html
-
《redis設計與實現 3.0版本》 http://redisbook.com/index.html
-
redis源碼解讀 3.2.8版本 https://blog.csdn.net/men_wen/article/details/75668345
本篇文章由一文多發平臺ArtiPub自動發佈