redis中的不同線程
redis單線程是指什么?
redis的所有命令處理都在同一個線程中完成
redis為什么采用單線程?
redis中存在多種數據結構存儲value,如果采用多線程,加鎖會很復雜、加鎖力度不阿紅控制,同時,采用多線程涉及頻繁的cpu上下文切換,抵消多線程的優勢。
redis 存儲結構
redis采用hashtable組織其存儲的kv數據
hashtable字典實現
redis 中 hashtable 組織是通過字典來實現的;hash 結構當節點超過 512 個或者單個字符串長度大于 64 時,hash 結構采用字典實現
數據結構
typedef struct dictEntry {void *key;union {void *val;uint64_t u64;int64_t s64;double d;} v;struct dictEntry *next;} dictEntry;typedef struct dictht {dictEntry **table;unsigned long size;// 數組長度
unsigned long sizemask; //size-1unsigned long used;//當前數組當中包含的元素
} dictht;typedef struct dict {dictType *type;void *privdata;dictht ht[2];long rehashidx; /* rehashing not in progress if rehashidx == -1 */int16_t pauserehash; /* If >0 rehashing is paused (<0 indicates coding error)
用于安全遍歷*/} dict;
沖突
抽屜原理 :n+1個蘋果放在 n 個抽屜中,蘋果最多的那個抽屜至少有 2 個蘋果;64位整數遠大 于數組的長度,比如數組長度為 4,那么 1、5、9、1+4n 都是映射到1號位數組;所以大概 率會發生沖突
抽屜就是hashtable,蘋果就是對應k-v對。
負載因子
負載因子 = used / size ;
used 是數組存儲元素的個數,size是hashtable大小
負載因子越小,沖突越小;負載因子越大,沖突越大;
redis 的負載因子是 1;
擴容
如果負載因子 > 1,為了避免頻繁的hash沖突,則會發生擴容,即申請一個新的的數組;擴容的規則是翻倍;
如果正在 fork(在 rdb、aof 復寫以及 rdb-aof 混用情況下)時,會阻止擴容;但是此時若負載 因子 > 5,索引效率大大降低, 則馬上擴容;這里涉及到寫時復制原理
擴容后的漸進式rehash
rehash的原因
擴容后,hashtable的size發生改變,基于原來size的映射規則不在適用
rehash步驟
將 ht[0] 中的元素重新經過 hash 函數生成 64 位整數,再對 射到 ht [1] ;
漸進式rehash的原因
當 hashtable 中的元素過多的時候,不能一次性 rehash 到 ht[1] ;這樣會長期占用 redis,其他 命令得不到響應;所以需要使用漸進式 rehash;
漸進式規則
?1. 分治的思想,將 rehash 分到之后的每步增刪改查的操作當中;
2. 在定時器中,最大執行一毫秒 rehash ;每次步長 100 個數組槽位;
處于漸進式 rehash 階段時,不會會發生擴容縮容
縮容
如果負載因子 < 0.1 ,則會發生縮容;縮容的規則是恰好包含 used 的 ;
恰好的理解:假如此時數組存儲元素個數為 9,恰好包含該元素的就是 ,也就是 16;
redis-value存儲轉換
跳表
理想跳表到概率跳表
redis跳表
redis IO多線程處理
為什么要采用IO多線程?
多個連接同時發送數據到redis時,會導致IO密集操作(read和send)和cpu密集型操作(decode和encode),如果采用單線程處理,就只能串行的處理每個連接的數據,但是read、decode、encode和send是可以并行執行的。
處理流程
1.主線程將IO密集型任務和cpu密集型任務放入全局隊列中,并分配給每個IO線程并行執行。
2.所有的命令處理由一個線程單獨執行
3.結果處理再交由每個IO線程執行。
redis的單線程為什么高效?
1.采用hashtable,使其增刪改查操作時間復雜度為O(1),同時采用漸進式rehash,避免長時間對redis的占用。
2.對不同value采用高效的數據結構,同時基于節點數量會對數據結構進行切換。
3.采用高效的reactor網絡模型,基于IO多路復用,實現對網絡多客戶端連接的快速響應。
柔性數組
https://github.com/0voice