《Redis設計與實現(xiàn)》筆記

1、 SDS

  • 常數(shù)復雜度獲取字符串長度

  • 記錄自身長度,避免緩沖區(qū)溢出

  • 減少修改字符串時帶來的內(nèi)存重分配次數(shù):空間預分配,惰性空間釋放

  • 二進制安全

    只關(guān)心二進制化的字符串,不關(guān)心具體格式,只會嚴格的按照二進制的數(shù)據(jù)存取,不會妄圖以某種特殊格式解析數(shù)據(jù)。

  • 兼容部分 C 字符串函數(shù)

2、跳表

組成:zskiplist、zskiplistNode

復雜度:Olg(N)、最壞O(N)

有序集合鍵的底層實現(xiàn)之一、集群。

前進指針:遍歷

跨度:計算排位 (Rank),在查找某個節(jié)點的過程中,將沿途訪問過的所有層的跨度累加起來,得到的結(jié)果就是目標節(jié)點在跳躍表中的排位。

每個節(jié)點的層數(shù)是 1~32之間的隨機數(shù)

同一跳躍表中,多個節(jié)點可以包含相同的分值,但每個節(jié)點的成員對象必須是唯一的

跳躍表中的節(jié)點按照分值大小進行排序,當分值相同時,節(jié)點按照成員對象的大小進行排序

3、字典

鏈地址法解決鍵沖突

漸進式 hash: h[0]、h[1]

4、垃圾回收

引用計數(shù)

對象共享:共享值為 0~9999的字符串對象

5、過期鍵刪除策略

  • 定時刪除:存在大量待刪除過期鍵時占用較多CPU時間,影響服務器的響應時間和吞吐量

Redis 采用策略

  • 惰性刪除:讀寫指令前執(zhí)行 expireIfNeeded 函數(shù)檢查鍵是否過期

    過期鍵如果不被刪除,則占用內(nèi)存不釋放。浪費內(nèi)存,有內(nèi)存泄漏風險 。

  • 定期刪除:expires 字典中隨機檢查一部分鍵的過期時間,并刪除過期鍵。

主服務器刪除一個過期鍵后,向從服務器發(fā)送 DEL 指令,顯式地刪除過期鍵,從服務器不會主動刪除過期鍵,需要等待主節(jié)點發(fā)送 DEL 命令,保證數(shù)據(jù)的一致性。

6、 數(shù)據(jù)庫

由 dict 和 expires 組成,dict 字典負責保存鍵值對,expires 字典保存鍵的過期時間

所有數(shù)據(jù)庫保存在 redisServer.db 中,數(shù)據(jù)庫數(shù)量由redisServer.dbnum 保存

客戶端通過修改目標數(shù)據(jù)庫指針,讓它指向 redisServer.db 數(shù)組中的不同元素來切換不同數(shù)據(jù)庫。

7、RDB

保存所有鍵值對數(shù)據(jù),壓縮二進制文件

SAVE 阻塞主進程,BGSAVE fork 子進程負責創(chuàng)建 rdb 文件,不阻塞。

8、AOF

保存所有寫命令。BGREWRITEAOF 重寫 AOF 文件,減小 AOF 文件大小 。

子進程執(zhí)行重寫

父進程可以繼續(xù)處理命令請求

子進程帶有服務器進程的數(shù)據(jù)副本,使用子進程而不是線程,可以避免在使用鎖的情況下,保證數(shù)據(jù)的安全性。

子進程完成 AOF 重寫后,向父進程發(fā)送信號,父進程調(diào)用信號處理函數(shù)(阻塞)并執(zhí)行以下工作:

  • 將 AOF 重寫緩沖區(qū)中的所有內(nèi)容寫入到新的 AOF 文件,對新的 AOF 文件進行改名,原子地覆蓋現(xiàn)有的 AOF 文件,

  • 完成新舊兩個 AOF 文件的替換。

數(shù)據(jù)一致性

執(zhí)行 BGREWRITEAOF 時,Redis 服務器維護一個 AOF 重寫緩沖區(qū),該緩沖區(qū)會在子進程創(chuàng)建新 AOF 文件期間,

記錄服務器執(zhí)行的所有寫命令。當子進程完成創(chuàng)建新的 AOF 文件工作之后,服務器會將重寫緩沖區(qū)的所有內(nèi)容追加到新 AOF 文件的末尾,使得新舊兩個 AOF 文件所保存的數(shù)據(jù)庫狀態(tài)一致。最后,服務器用新的 AOF 文件替換舊的 AOF 文件 ,以此來完成 AOF 文件的重寫。

客戶端(發(fā)送命令) > 命令處理器 (追加命令)> AOF 緩沖區(qū) 、AOF 重寫緩沖區(qū)

9、事件

  • 文件事件

    處理并發(fā):I/O 多路復用程序?qū)⑺挟a(chǎn)生事件的套接字放到一個隊列里。

  • 時間事件(存放在鏈表中, 屬性:id、when、timeProc(函數(shù)) )

定時事件:讓程序在指定的時間之后執(zhí)行一次

周期事件:讓程序每隔指定時間執(zhí)行一次

10、客戶端

服務器端維護 clients 鏈表保存所有客戶端的狀態(tài)

11、同步

PSYNC 命令(新),完整重同步(初次復制)、部分重同步(斷線后重復制)

部分重同步三要素

復制偏移量

復制積壓緩沖區(qū)(replication backlog):如果 offset 偏移量之后的數(shù)據(jù)仍在 replication backlog 中,執(zhí)行部分重同步;否則執(zhí)行完整重同步。

服務器 run ID:若斷線恢復,主服務器 run ID 不變,執(zhí)行部分重同步;否則執(zhí)行完整重同步。

12、 Sentinel

兩個與主服務器的異步網(wǎng)絡連接

  • 命令連接,用于向主服務器發(fā)送命令,并接收回復

  • 訂閱連接,訂閱主服務器的 Sentinel:hello 頻道

每 10s 向主服務器發(fā)送 INFO 命令,獲取服務器信息

主觀下線 (SRI_S_DOWN,在 down-after-milliseconds 時間內(nèi),連續(xù)向Sentinel返回無效回復)-> 客觀下線(足夠多主觀下線投票)

min-salves-to-write 1:至少向一個 slave 節(jié)點寫數(shù)據(jù),避免 master 網(wǎng)絡隔離后繼續(xù)寫數(shù)據(jù),造成數(shù)據(jù)不一致。

13、Cluster

16384 個槽、Gossip 協(xié)議

單個 master (無 slave)掛掉,則整個集群掛掉,可設置 cluster-require-full-coverage no 解決

bgsave 打開,多個實例同時 fork ,響應時間增大(關(guān)閉 bgsave,開 aof)

依賴客戶端成熟度(智能客戶端)

失效檢測:

ping -> PFAIL -> FAIL

14、事務

將多個命令請求打包(隊列),一次性、按順序執(zhí)行多個命令。

單線程串行執(zhí)行事務,保證隔離性。

15、SORT 實現(xiàn)

根據(jù)數(shù)據(jù)項的 u.score 排序

(來源:B站團隊分享 http://xargin.com/weekend/

單線程 mgetall,或者 hgetall 的時候會阻塞后續(xù)的調(diào)用

解決:redis 只拿來操作一些復雜的數(shù)據(jù)結(jié)構(gòu),比如 sorted set 之類的數(shù)據(jù),可以拿來用 score 做排序,用吞吐量更好的多線程 memcached 來做 kv 緩存。

zadd的時候key已經(jīng)過期了,導致一些看起來匪夷所思的bug之類的

解決:用expire then zadd的方式巧妙地解決了這些問題。
文章同步公眾號:wuqxuan

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容