目錄
存儲結構
?存儲轉換
數據組織
hash 沖突
?負載因子
擴容
?縮容
?漸進式rehash
?Redis 線程模型
單線程命令處理機制
為什么Redis 命令的單線程快
機制
優化
柔性數組
?Redis reactor_io 多線程網絡模型
存儲結構
- key-value鍵值對通過 hash 的方式存儲到數組中
- value 主要的數據結構有string,list,hash,set,zset
- value 的數據不是單一類型,可以嵌入 key - value 鍵值對
?
?存儲轉換
? ? ? ? redis 在進行編碼存儲的時候,同一類型不同的數據量會導致編碼的方式不同
- 數據量少的時候,以存儲效率高為主
- 數據量多的時候,以運行查詢快為主
數據組織
????????redis 的 KV 數據組織方式是字典,通過 hashtable 來實現。hashtable 就是通過 hash 運算的方式來決定字符串放到數組的哪一個槽位,數組的大小根據數據量來進行調整,所以就會涉及到擴容和縮容。
- 字符串經過 hash 函數運算得到 64 位整數
- 64 位整數%表長size得到余數,即是字符串在數組中的槽位
- 將數組存入對應的槽位中
hash 沖突
? ? ? ? ?當存儲的數據個數大于數組的長度時,必將發生沖突,就是在一個槽內存了兩個或多個數據。
抽屜原理: n+1個蘋果放在 n 個抽屜中,蘋果最多的那個抽屜至少有 2 個蘋果;64位整數遠大 于數組的長度,比如數組長度為 4,那么 1、5、9、1+4n 都是映射到1號位數組;所以大概 率會發生沖突;
?負載因子
負載因子 = used / size ,?used 是數組存儲元素的個數,size 是數組的長度;
負載因子越小,沖突越小;
負載因子越大,沖突越大;
redis 的負載因子是 1
擴容
?如果負載因子 > 1,則會發生擴容,擴容的規則是翻倍擴容;
如果正在 fork (在 rdb、aof 復寫以及 rdb-aof 混用情況下)時,會阻止擴容,但是此時若負載 因子 > 5,索引效率大大降低, 則馬上擴容;
?擴容的時候,會發生rehash,擴容后每個元素可能會有兩個位置出現。
?縮容
如果負載因子 < 0.1 ,則會發生縮容,縮容的規則是恰好包含 used 的 2的n次方;
理解如下:假如此時數組存儲元素個數為 9,恰好包含該元素的就是 2的4次方,也就是 16;
縮容也會發生rehash,位置的調整與擴容相反。
?
?漸進式rehash
當 hashtable 中的元素過多的時候,不能一次性 rehash 到 ht[1] ,這樣會長期占用 redis,其他 命令得不到響應,所以需要使用漸進式 rehash;
rehash步驟:
? ? ? ? 將 ht[0] 中的元素重新經過 hash 函數生成 64 位整數,再對ht[1] 長度進行取余,從而映射到 ht[1]。
漸進式 rehash 規則:
? ? ? ? 1. 分治的思想,將 rehash 分到之后的每步增刪改查的操作當中;
? ? ? ? 2. 在定時器中,閑時最大執行一毫秒 rehash ,每次步長 100 個數組槽位。
處于漸進式 rehash 階段時,是否會發生擴容縮容?不會!
?Redis 線程模型
????????Redis采用"單線程處理命令+多線程處理后臺任務"的混合模型。核心命令執行保持單線程特性,通過多線程處理網絡I/O、持久化等輔助操作。
單線程命令處理機制
前提:單線程不能有耗時操作
1.避免加鎖復雜度
- 多線程會引入并發沖突,需要加鎖控制:
- 加鎖粒度難把控,鎖得多了性能下降,鎖得少了數據錯亂
- 容易出現死鎖、競態等問題
- 代碼復雜度和維護成本上升
2.避免線程切換開銷
- 多線程會發生頻繁的 CPU 上下文切換:
- 線程切換需要保存/恢復寄存器等上下文信息
- 如果 Redis 每條命令都切換線程,反而會拉低整體性能
3.數據結構無需加鎖
- 單線程訪問所有核心數據結構(如字典、跳表等),天然線程安全
- 操作原子性強,不會出現“半更新”的問題
- 命令順序可控,執行邏輯簡單可靠
為什么Redis 命令的單線程快
機制
特點 | 說明 |
內存數據庫 | 所有數據存儲在內存中,讀取/寫入速度極快 |
高效數據結構 | 字典,跳表,壓縮列表,整數集合,quicklist,按需切換,空間與效率均衡 |
數據組織機制優化 | 漸進式 Rehash,按需擴容,避免一次性耗時操作 |
Reactor 網絡模型 | 使用多路復用(epoll/select/poll),IO 高效 |
單線程無鎖設計 | 所有命令串行執行,避免加鎖帶來的性能損耗和線程切換開銷 |
優化
1.分治
- ? ?將rehash分布在每次操作中
- ? ?定時器 + 空閑時間處理機制
2. 耗時阻塞操作在其他線程中處理
操作 | 線程方式 |
Lazy Free | 創建后臺線程異步刪除大 Key |
AOF Rewrite | 在子進程中異步執行 |
RDB Save | 在子進程中快照,不阻塞主線程 |
Redis 6.0 I/O 多線程 | 網絡I/O在多線程中完成,提高并發性能 |
3.對象類型采用不同的數據結構實現
柔性數組
C99 引入的一種結構體末尾的數組字段,用于實現可變長度數組
- 柔性數組的大小是在 運行時動態決定的
- 它不占結構體靜態大小,而是結構體分配內存時追加在后面的一段連續空間
struct MyStruct {int id;char name[20];int data[]; // 柔性數組成員,必須是結構體的最后一個字段
};
?Redis reactor_io 多線程網絡模型
- IO密集型程序:主要等待外部資源
- CPU密集型程序:主要占用CPU計算資源
Redis 主線程管理調度 + 執行命令,IO 線程專職網絡通信,讓系統性能和單線程一致性兩者兼得?