鏈表在redis應用很是普遍,好比當列表鍵包含了數量比較多的元素,又或者列表中包含的元素都是比較長的字符串,redis就會使用鏈表做爲列表鍵的底層實現html
該結構中的type定義了特定類型鍵值對的函數,trehash定義了是否正在rehash,由於redis的rehash是漸進式的,漸進式的hash要操做2個dictht結構redis
分鍵值對和(next)指向下一個節點的指針(用於解決鍵的衝突)算法
又dictEntry的數組,還有哈希表的大小,用於hash的sizemask,和節點數量數據庫
或者負載因子小於0.1系統會自動收縮數組
跳躍表按照分值來進行排序,因此基本redis只有有序列表和內部數據採用這個結構緩存
typedef struct zskiplist {
//指向跳躍表的表頭和表尾
structs zkiplistNode *header, *tail;
//節點數量
unsigned long length;
//最大節點的基數
int level;
}
複製代碼
壓縮列表就是相似數組,可是自己結構存儲了自身的長度和其餘信息,結尾有個0xff來表示結尾,而後中間的數據都有本身的長度標識,其中previous_entry_length用來標識前一個數據的長度,encoding用來標識自己數據的長度,其中以11開始標識是整數型,而01 00 10用來標識字節數組,後續的位用來表示長度,content用來表示內容服務器
redis不是直接拿上面提到的數據結構來實現鍵值對數據庫,而是建立了一個對象系統,用上面的數據結構來組成 網絡
可使用type命令查看其鍵的值的類型,好比type price
數據結構
可使用object encoding命令查看鍵的值的編碼,好比object encoding price
列表對象由ziplist或者linkedlist編碼
同時知足一下兩個條件的時候列表使用ziplist編碼
哈希對象使用ziplist或者hashtable來編碼
當知足兩個條件時,哈希對象使用ziplist編碼
集合對象的編碼能夠是intset或者hashtable
當集合對象都是整數值,並且數量小於512個就會使用intset編碼
有序集合編碼能夠是ziplist或者skiplist
當使用skiplist編碼的時候是會使用zset結構,該結構包含了skiplist和字典來實現,由於字典能夠很快定位成員的分值,可是作區間計算的時候沒法進行只能進行排序後獲取,而skiplist做爲支持有序集合查找和範圍操做盡能夠快的執行,因此redis使用字典和跳躍表來實現有序集合
當元素數量小於128個,且成員的長度都小於64字節的時候使用ziplist
由於c語言不具有內存回收功能,因此redis本身實現了一套引用計數的回收機制
在回收機制以外,由於引入了對象引用計數的機制,而後對象能夠在2個鍵中進行共享,當他們的值相同的時候他們的值能夠指向同一塊內存
redisObject中除了上面提到的對象還有lru這個屬性,該屬性記錄了最後一次命令訪問的時間,該空轉時長還有另一項做用,就是服務器設定了某種內存回收機制,就是當內存超過了maxmemory,空轉時長比較高的那部分會優先丟棄從而回收內存
struct redisServer{
// ...
redis *db;
int dbnum;
// ...
}
typedef struct redisDb{
// ...
dict *dict
dict *expries
// ...
}
複製代碼
redis有4個命令能夠設置過時時間, expire pexpire expireat pexpireat, 本質上最終都是使用pexpireat命令來設置過時時間,不論是相對時間仍是絕對時間,最終都是用絕對時間來進行設置,而後過時時間保存和key-value是不同的,用的是redisDb中的expreies
rdb有3種
dirty,saveparams和lastsave都是redisServer的屬性,一個用來記錄在備份期間有多少數據更新了,而lastsave是最後更新時間,saveparams用來記錄定時定量備份用到的時間和更新次數從而知道何時能進行更新
AOF的本質就是將每一條命令進行持久化,包括選擇數據庫的命令,而後有3種模式將AOF進行同步到磁盤的選項
aof重寫就是假若有不少命令都是操做同一個key,那麼重寫的過程就會直接從數據庫中拿到那個key的value, 而後生成一條這個value的命令來代替原來的多條命令,而後在重寫期間,redis會建立一個子進程,由於子進程用的是進程的數據副本,不會有鎖的衝突,這就有個問題就是主進程還在進行接收客戶端命令,這邊就須要一個臨時的重寫緩衝區來存儲最新的命令,當子進程重寫完成後,將緩衝區的命令追加到aof文件結尾就ok了,這樣重寫後的數據纔不會不一致
服務端有一個指針用鏈表的形式保存了全部客戶端的屬性(也就是客戶端的對象)
typeof struct redisClient {
// ...
int fd;
robj *name;
int flag;
sds querybuf;
robj **argv;
int argc;
struct redisCommand *cmd;
char buf[REDIS_REPLY_CHUNK_BYTES];
int bufpos;
list *reply;
int authenticated;
time_t ctime;
time_t lastinteraction;
time_t dbuf_soft_limit_reached_time;
// ...
}
複製代碼
psync就增長2種模式
對於部分重同步,psync是這樣設計的,每一個服務都有一個偏移量,而主服務上有個複製擠壓緩衝區,也就是下標是偏移量對應內容,而後各個服務之間會進行偏移量對比,發現少的時候從緩衝區裏拿,發現緩衝區都找不到就直接徹底重同步,而後還假如服務id這個字段,當從重連到主的時候發現id不同直接徹底重同步
每一個客戶端都有一個multistate msstats的屬性用來表示事務狀態
typedef struct multistate{
// ...
multiCmd *commands;
int counts;
// ...
}
typedef struct multiCmd {
// ...
robj **argv;
int argc;
struct redisCommond *cmd;
// ...
}
複製代碼
watch本質其實就是在redisDb中有一個字典表,裏面對應的就是監聽的key, 在進行命令操做對應的key的時候會去監聽字典表中看是否有客戶端監聽,當有監聽就會去打開對應的客戶端的REDIS_DIRTY_CAS的flag, 當事務看到flag是打開的時候就會報錯這行命令
redis的事務本質就是保證一致性和隔離性,而原子性由於沒有回滾一行命令出錯不會影響下面的命令執行,redis將這種錯誤稱之爲程序錯誤,不該該出如今生產環境,而持久性是根據rdb和aof的策略來斷定的,當每次事務完成都會進行落盤的操做就能夠擁有持久性,而隔離性由於是單線程因此天生具備隔離性,一致性是說數據沒有錯誤和非法數據,由於redis的單挑命令是原子的,當是命令錯誤的時候這條命令不會執行保證了一致性,當服務宕機的時候,無論有沒有被落盤數據都不會錯誤,要不就是丟失,而後就是入隊錯誤也就是非法命令,redis會在事務以前檢驗全部命令,因此不會有問題