Redis設計與實現(一) 分散式部分 複製 和 釋出/訂閱模式
Redis設計與實現
第一部分 資料結構與物件
Redis物件
-
首先key value,key是固定的字串物件,value可以是那5種中的一種,而那5種根據場景的不同,每種都有至少兩種編碼方式,也就是資料結構
-
資料結構有linkedlist 雙端連結串列
-
ziplist壓縮列表
- 這個用的太多了 以至於我有深刻的印象
-
skiplist
- 跳躍表 類似於平衡樹的作用 但是實現方式太友好了
-
raw
- 都是sds simply dynamic string
-
embstr
- 都是sds simply dynamic string 區別是這個更短嘍 壓縮過的 是連續的記憶體 所以速度比raw的快
-
hashtable
- 兩個表嘛ht[0]和ht[1]
- 嗯 衝突就是在同一個雜湊值組成連結串列 然後有一個負載因子 預設好像是1 進行rehash 然後rehasn的話會在另外一個大於當前數量number的最小的2的n次方那麼大的擴容 擴容期間 伺服器空閒就轉移 當然 新插入的都是在這個新表裡 hashtable陣列ht[1] 然後全部拷到ht[1]後 就會把ht[0]換成ht[1] 然後h[1]又變成空
-
linkedlist
- 就是一個簡單的雙鏈表
-
intset
- int_8 int_16 int_32 int_64
- 會儲存編碼方式
- 一旦有東西需要往上提 比如從int_32到int_64了 這個過程是不可逆的 即使把64的都刪了 也不會降編碼回32了
第八章 物件
-
字串
-
int
- redis預設有0~9999的int字串
- 順便說一下 整數都是先轉字串 然後用的時候再轉回數字
-
raw
-
embstr
-
-
列表
- linkedlist
- ziplist
-
雜湊物件
- ziplist
- dict 或者叫hashtable
-
集合
- intset
- hashtable
-
有序集合
-
ziplist
- score key
-
資料結構叫zset
-
skiplist
- 子主題 1
-
dict
- 子主題 1
-
同時用這兩個的原因是因為 既要單點查詢的速度 也要範圍查詢的速度吧
-
-
第二部分 單機資料庫的實現
第9章 資料庫
-
型別檢查 命令多型
-
先看key是不是符合那個命令的
- 再看看編碼方式是什麼
-
-
物件回收
- 計數唄
-
物件共享
-
不共享包含字串的物件
-
對整數檢查O(1)
-
對字串O(N)
-
物件包含了多個值物件
- O(N^2)
-
-
空轉時長
- 有個lru時間 記錄最後一次被程式訪問的時間
第10章 RDB持久化
第11章 AOF持久化
第12章 事件
第13章 客戶端
第14章 伺服器
第三部分 多機資料庫的實現
第15章 複製
-
Redis的複製分為兩步, 同步(sync)和傳播(command propagate)
- 在我理解無非一個是先獲取一個伺服器的快照
- 另外一個是追趕式恢復
-
1.同步
-
客戶端向伺服器傳送SYNC命令來完成
- 1.從伺服器向主伺服器傳送SYNC命令
- 2.收到SYNC的master執行 BGSAVE命令, 在後臺生成一個RDB檔案, 並用一個緩衝區來記錄現在開始的所有命令
- 3.主伺服器把RDB檔案傳給從伺服器,從服務區更新至 主伺服器執行BGSAVE的命令狀態
- 4.主伺服器把緩衝區的所有命令傳送給從伺服器, 從伺服器進行追趕式恢復
-
SYNC命令非常號資源,
- 生成RDB檔案需要CPU 記憶體, 磁碟I/O
- 傳送RDB檔案需要網路頻寬
- 從節點載入的時候也無法處理請求
-
-
2.命令傳播
- 無非就是主伺服器執行命令 , 然後傳送給 從伺服器 來保持一致性
-
舊版複製功能的缺陷
-
每次都是把完整的RDB檔案進行傳輸
- 如果斷線時間不是很長也要 全部複製一遍
- 所有要引入部分複製(PSYNC)
-
-
新版複製功能
-
部分重同步的實現
-
1.主伺服器的複製偏移量(replication offset)
-
主伺服器和從伺服器都維護一個偏移量
- 每次主伺服器向從伺服器傳播N個位元組的資料,就把位元組的偏移量加上N
- 從伺服器收到N個位元組的資料, 也把自己的偏移量加上N
- 如果二者偏移量不一樣, 那就是資料不一致
-
-
2.主伺服器的複製擠壓緩衝區(replication backlog)
-
是一個固定長度的先進先出佇列
-
如果從伺服器連線上主伺服器的時候, 從伺服器會把自己的偏移量發給伺服器
-
如果已經不在了
- 執行完整的重同步
-
如果這個偏移量還在複製積壓緩衝區之內
- 執行部分同步
-
-
預設1MB
- 一般設成= 2(冗餘)* 平均重連時間*每秒寫入命令的位元組數
-
-
-
3.伺服器的執行ID
-
主伺服器會把自己的ID傳送給客戶端
- 如果從伺服器儲存的執行ID和當前的主伺服器發過來的一樣, 說明之前連的就是這臺, 嘗試部分重同步
- 如果不一樣, 執行完整重同步
-
-
-
-
複製功能的實現
-
步驟1 設定主伺服器的ip和埠
- 加上倆機子伺服器
- 非同步的, 返回ok, 然後後臺執行復制
-
步驟2 建立socket連線
-
如果成功連線
-
從伺服器會被主伺服器看成特殊的客戶端
-
對於普通的客戶端來說
- 從伺服器還是個伺服器
-
-
-
步驟3 傳送ping命令
- 如果不成功會回到第2步的
-
步驟4 身份驗證
- 主要是看主伺服器和從伺服器有沒有設定密碼, 常識
- 失敗就重試
- 成功就準備複製
-
步驟5 傳送埠資訊
- 從伺服器向主伺服器傳送從伺服器的監聽埠號
-
步驟6 同步
- 互為客戶端
-
步驟7 命令傳播
-
進入命令傳播狀態
- 一直接受主伺服器的寫命令
-
-
-
心跳檢測
-
命令傳播階段, 每1s ,從伺服器會向主伺服器傳送replication ack [offset]
-
作用
-
1.檢測主從伺服器的連線狀態
-
2.輔助實現min-slaves選項
- 比如min-slave-to-write 3
- min-salves-max-lag 10
- 如果從伺服器少於3 , 拒絕寫
- 如果三個伺服器延遲都大於等於10, 拒絕寫的請求
-
3.檢測命令丟失
- 如果主伺服器發現落後於自己, 就會從複製擠壓緩衝區中重新發給從伺服器
-
-
第16章 Sentinel哨兵
第17章 叢集
第四部分 獨立功能的實現
第18章 釋出與訂閱
-
釋出與訂閱的核心命令是兩組
-
Publish
-
Subsctibe
- Subscribe
- UnSubscribe
- PSubscribe
- PUnSubscribe
-
-
1.頻道的訂閱與退訂
-
訂閱頻道
-
伺服器有個字典叫pubsub_channels
- 鍵是 某個被訂閱的頻道
- 值是 一個連結串列, 這裡面存了該頻道的訂閱者
-
-
退訂頻道
-
根據頻道的名字在字典中找
- 把客戶端從連結串列中刪除
- 如果恰好是最後一個, 把這個頻道也刪除
-
-
-
2.模式的訂閱與退訂
-
伺服器有個list叫pubsub_patterns
- <client, pattern>作為list連結串列的元素
- list<client,pattern>
-
訂閱模式
- 新建一個node, 把這個node新增到list後面
-
退訂模式
- 遍歷這個list, 找到node然後刪掉
-
-
3.傳送訊息
-
傳送訊息publish channelA "message"有兩步,
- 1.首先把訊息發給channelA的所有訂閱者
- 2.然後把訊息發給所有模式匹配channelA的
-
1.很簡單啦, 找到key對應的連結串列, 然後全部發一遍
-
2.先查詢所有list裡的pattern, 如果匹配, 就把這個pattern對應的client都發一遍
-
-
4.檢視訂閱訊息
-
pubsub channels
-
pubsub numsub [channel]
- 返回channel的訂閱者數量
-
pubsub numpat
- 返回被訂閱模式的數量
- number of pattern
-
第19章 事務
第23章 慢查詢日誌
第24章 監視器
- 客戶端可以通過MONITOR命令 ,將客戶端轉換成監視器 ,接受並列印每個命令請求的相關資訊
- 把客戶端的REDIS_MONITOR標識開啟, 就可以從普通客戶端變成監視器了
- 伺服器把所有監視器都存在一個連結串列裡
- 每次都會遍歷連結串列, 挨個傳送
實踐
Redis的入門應用
-
Redis是key-value型資料庫
-
Redis 裡的單行命令都是原子的 是為了同時有多個使用者對同一個資料修改
-
string
-
set key value
-
子主題 1
-
SET server:name "fido"
-
GET server:name => "fido"
-
EXISTS server:name => 1
-
EXISTS server:blabla => 0
-
SET connections 10
-
INCR connections => 11
-
INCR connections => 12
-
DEL connections
-
INCR connections => 1
-
It is also possible to increment the number contained inside a key by a specific amount:
-
INCRBY connections 100 => 101
-
And there are similar commands in order to decrement the value of the key.
-
DECR connections => 100
-
DECRBY connections 10 => 90
-
-
-
get key
-
exists key
-
DEL key
-
EXPIRE key 100
-
100秒後刪除
-
TTL key
- 顯示還有幾秒刪除
- 只要set key 一次 之前的計時失效 TTL key 返回-1
- 返回-2說明計時過了 這東西被刪了
-
PERSIST key
-
讓這個計時結束
- 永久儲存
-
-
-
INCR key
- INCRBY key 100
-
DECR key
- DECRBY key 100
-
不能簡單的get key \ value=value+1 \set key value
-
-
list
-
對頭尾操作較快 且 有序號的
-
lpush friend "yanhao"
-
rpush friend "yanhao"
-
lpush friend 1 2 3 4
-
執行順序是
- lpush friend 1
- lpush friend 2
- lpush friend 3
- lpush friend 4
-
所以結果是 4 3 2 1
-
-
lrange friend 0 -1
-
切片
-
example
-
LRANGE friends 0 -1 => 1) "Sam", 2) "Alice", 3) "Bob"
-
LRANGE friends 0 1 => 1) "Sam", 2) "Alice"
-
LRANGE friends 1 2 => 1) "Alice", 2) "Bob"
-
lrange friend 0 10
-
- "Sam", 2) "Alice", 3) "Bob"
-
-
lrange friend -3 -1
-
- "Sam", 2) "Alice", 3) "Bob"
-
-
-
-
lset friend 0 "yanhao"
- 把friend這個list裡面的第0個元素改成"yanhao"
-
lpop friend
- rpop friend
- 子主題 2
-
llen
-
-
set
-
sadd setname "yanhao"
-
return 0
- 沒加成功,因為本來就有這個key了
-
return 1
- 加成功了
-
-
sismember setname "yanhao"
-
smembers friend
-
sincr
-
sdecr
-
srem friend "yanhao"
- return 0
- return 1
-
srandmeber friend 2
- 隨機返回資料成員
-
spop
- 隨機刪
-
sunion set1 set2
-
-
sorted set
-
在set的同時加上一個用來排序的東西
-
ZADD fruit 1 apple
-
ZADD fruit 2 banano
-
Zrange fruit 0 1
- 切片
-
-
hashes
-
hset user:37 name "yanhao"
-
hgetall user:37
-
hmset user:37 name "yanhao" age 17
- 同時設定多個
-
hget user:37 name
-
hincrby user:37 name 2
- name會加2
-
hdel
-
XMind: ZEN - Trial Version