1、 SDS
常數(shù)復(fù)雜度獲取字符串長度
記錄自身長度,避免緩沖區(qū)溢出
減少修改字符串時(shí)帶來的內(nèi)存重分配次數(shù):空間預(yù)分配,惰性空間釋放
-
二進(jìn)制安全
只關(guān)心二進(jìn)制化的字符串,不關(guān)心具體格式,只會(huì)嚴(yán)格的按照二進(jìn)制的數(shù)據(jù)存取,不會(huì)妄圖以某種特殊格式解析數(shù)據(jù)。
兼容部分 C 字符串函數(shù)
2、跳表
組成:zskiplist、zskiplistNode
復(fù)雜度:Olg(N)、最壞O(N)
有序集合鍵的底層實(shí)現(xiàn)之一、集群。
前進(jìn)指針:遍歷
跨度:計(jì)算排位 (Rank),在查找某個(gè)節(jié)點(diǎn)的過程中,將沿途訪問過的所有層的跨度累加起來,得到的結(jié)果就是目標(biāo)節(jié)點(diǎn)在跳躍表中的排位。
每個(gè)節(jié)點(diǎn)的層數(shù)是 1~32之間的隨機(jī)數(shù)
同一跳躍表中,多個(gè)節(jié)點(diǎn)可以包含相同的分值,但每個(gè)節(jié)點(diǎn)的成員對(duì)象必須是唯一的
跳躍表中的節(jié)點(diǎn)按照分值大小進(jìn)行排序,當(dāng)分值相同時(shí),節(jié)點(diǎn)按照成員對(duì)象的大小進(jìn)行排序
3、字典
鏈地址法解決鍵沖突
漸進(jìn)式 hash: h[0]、h[1]
4、垃圾回收
引用計(jì)數(shù)
對(duì)象共享:共享值為 0~9999的字符串對(duì)象
5、過期鍵刪除策略
- 定時(shí)刪除:存在大量待刪除過期鍵時(shí)占用較多CPU時(shí)間,影響服務(wù)器的響應(yīng)時(shí)間和吞吐量
Redis 采用策略:
-
惰性刪除:讀寫指令前執(zhí)行 expireIfNeeded 函數(shù)檢查鍵是否過期
過期鍵如果不被刪除,則占用內(nèi)存不釋放。浪費(fèi)內(nèi)存,有內(nèi)存泄漏風(fēng)險(xiǎn) 。
定期刪除:expires 字典中隨機(jī)檢查一部分鍵的過期時(shí)間,并刪除過期鍵。
主服務(wù)器刪除一個(gè)過期鍵后,向從服務(wù)器發(fā)送 DEL 指令,顯式地刪除過期鍵,從服務(wù)器不會(huì)主動(dòng)刪除過期鍵,需要等待主節(jié)點(diǎn)發(fā)送 DEL 命令,保證數(shù)據(jù)的一致性。
6、 數(shù)據(jù)庫
由 dict 和 expires 組成,dict 字典負(fù)責(zé)保存鍵值對(duì),expires 字典保存鍵的過期時(shí)間
所有數(shù)據(jù)庫保存在 redisServer.db 中,數(shù)據(jù)庫數(shù)量由redisServer.dbnum 保存
客戶端通過修改目標(biāo)數(shù)據(jù)庫指針,讓它指向 redisServer.db 數(shù)組中的不同元素來切換不同數(shù)據(jù)庫。
7、RDB
保存所有鍵值對(duì)數(shù)據(jù),壓縮二進(jìn)制文件
SAVE 阻塞主進(jìn)程,BGSAVE fork 子進(jìn)程負(fù)責(zé)創(chuàng)建 rdb 文件,不阻塞。
8、AOF
保存所有寫命令。BGREWRITEAOF 重寫 AOF 文件,減小 AOF 文件大小 。
子進(jìn)程執(zhí)行重寫:
父進(jìn)程可以繼續(xù)處理命令請(qǐng)求
子進(jìn)程帶有服務(wù)器進(jìn)程的數(shù)據(jù)副本,使用子進(jìn)程而不是線程,可以避免在使用鎖的情況下,保證數(shù)據(jù)的安全性。
子進(jìn)程完成 AOF 重寫后,向父進(jìn)程發(fā)送信號(hào),父進(jìn)程調(diào)用信號(hào)處理函數(shù)(阻塞)并執(zhí)行以下工作:
將 AOF 重寫緩沖區(qū)中的所有內(nèi)容寫入到新的 AOF 文件,對(duì)新的 AOF 文件進(jìn)行改名,原子地覆蓋現(xiàn)有的 AOF 文件,
完成新舊兩個(gè) AOF 文件的替換。
數(shù)據(jù)一致性:
執(zhí)行 BGREWRITEAOF 時(shí),Redis 服務(wù)器維護(hù)一個(gè) AOF 重寫緩沖區(qū),該緩沖區(qū)會(huì)在子進(jìn)程創(chuàng)建新 AOF 文件期間,
記錄服務(wù)器執(zhí)行的所有寫命令。當(dāng)子進(jìn)程完成創(chuàng)建新的 AOF 文件工作之后,服務(wù)器會(huì)將重寫緩沖區(qū)的所有內(nèi)容追加到新 AOF 文件的末尾,使得新舊兩個(gè) AOF 文件所保存的數(shù)據(jù)庫狀態(tài)一致。最后,服務(wù)器用新的 AOF 文件替換舊的 AOF 文件 ,以此來完成 AOF 文件的重寫。
客戶端(發(fā)送命令) > 命令處理器 (追加命令)> AOF 緩沖區(qū) 、AOF 重寫緩沖區(qū)
9、事件
-
文件事件
處理并發(fā):I/O 多路復(fù)用程序?qū)⑺挟a(chǎn)生事件的套接字放到一個(gè)隊(duì)列里。
時(shí)間事件(存放在鏈表中, 屬性:id、when、timeProc(函數(shù)) )
定時(shí)事件:讓程序在指定的時(shí)間之后執(zhí)行一次
周期事件:讓程序每隔指定時(shí)間執(zhí)行一次
10、客戶端
服務(wù)器端維護(hù) clients 鏈表保存所有客戶端的狀態(tài)
11、同步
PSYNC 命令(新),完整重同步(初次復(fù)制)、部分重同步(斷線后重復(fù)制)
部分重同步三要素:
復(fù)制偏移量
復(fù)制積壓緩沖區(qū)(replication backlog):如果 offset 偏移量之后的數(shù)據(jù)仍在 replication backlog 中,執(zhí)行部分重同步;否則執(zhí)行完整重同步。
服務(wù)器 run ID:若斷線恢復(fù),主服務(wù)器 run ID 不變,執(zhí)行部分重同步;否則執(zhí)行完整重同步。
12、 Sentinel
兩個(gè)與主服務(wù)器的異步網(wǎng)絡(luò)連接
命令連接,用于向主服務(wù)器發(fā)送命令,并接收回復(fù)
訂閱連接,訂閱主服務(wù)器的 Sentinel:hello 頻道
每 10s 向主服務(wù)器發(fā)送 INFO 命令,獲取服務(wù)器信息
主觀下線 (SRI_S_DOWN,在 down-after-milliseconds 時(shí)間內(nèi),連續(xù)向Sentinel返回?zé)o效回復(fù))-> 客觀下線(足夠多主觀下線投票)
min-salves-to-write 1:至少向一個(gè) slave 節(jié)點(diǎn)寫數(shù)據(jù),避免 master 網(wǎng)絡(luò)隔離后繼續(xù)寫數(shù)據(jù),造成數(shù)據(jù)不一致。
13、Cluster
16384 個(gè)槽、Gossip 協(xié)議
單個(gè) master (無 slave)掛掉,則整個(gè)集群掛掉,可設(shè)置 cluster-require-full-coverage no 解決
bgsave 打開,多個(gè)實(shí)例同時(shí) fork ,響應(yīng)時(shí)間增大(關(guān)閉 bgsave,開 aof)
依賴客戶端成熟度(智能客戶端)
失效檢測(cè):
ping -> PFAIL -> FAIL
14、事務(wù)
將多個(gè)命令請(qǐng)求打包(隊(duì)列),一次性、按順序執(zhí)行多個(gè)命令。
單線程串行執(zhí)行事務(wù),保證隔離性。
15、SORT 實(shí)現(xiàn)
根據(jù)數(shù)據(jù)項(xiàng)的 u.score 排序
坑 (來源:B站團(tuán)隊(duì)分享 http://xargin.com/weekend/)
單線程 mgetall,或者 hgetall 的時(shí)候會(huì)阻塞后續(xù)的調(diào)用
解決:redis 只拿來操作一些復(fù)雜的數(shù)據(jù)結(jié)構(gòu),比如 sorted set 之類的數(shù)據(jù),可以拿來用 score 做排序,用吞吐量更好的多線程 memcached 來做 kv 緩存。
zadd的時(shí)候key已經(jīng)過期了,導(dǎo)致一些看起來匪夷所思的bug之類的
解決:用expire then zadd的方式巧妙地解決了這些問題。
文章同步公眾號(hào):wuqxuan