Redis |
描述 |
Redis |
Redis是一個開源的使用C語言編寫、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日志型寄雀、Key-Value數(shù)據(jù)庫 |
數(shù)據(jù)類型
/*
* SDS
*/
struct sdshdr {
int len;
int free;
char buf[];
};
String |
描述 |
String |
采用SDS結(jié)構(gòu)進行存儲 |
sds |
Redis在為一個字符串創(chuàng)建一個sds 對象時,通常會申請比字符串長度更長的字節(jié)數(shù)組盐肃,Redis將字符串保存進這個數(shù)組,同時在len這個變量保存字符串的長度权悟,再用free這個變量保存buf尚未使用的字節(jié)數(shù)量 |
內(nèi)存分配 |
通過申請更多的內(nèi)存砸王,減少字符串修改時內(nèi)存的分配次數(shù) |
二進制安全 |
C字符串不能包含空字符,因為先被程序讀入的空字符會被誤認為字符串的結(jié)束峦阁,只能保存純文本數(shù)據(jù)谦铃,sds 長度是根據(jù)len決定的,即使內(nèi)容中含有空字符串榔昔,也對數(shù)據(jù)的完整性也沒有任何影響 |
鏈表 |
描述 |
單鏈表 |
鏈表中的數(shù)據(jù)是以結(jié)點來表示的驹闰,每個結(jié)點的構(gòu)成:元素(數(shù)據(jù)元素的映象) + 指針(指示后繼元素存儲位置),元素就是存儲數(shù)據(jù)的存儲單元撒会,指針就是連接每個結(jié)點的地址數(shù)據(jù) |
優(yōu)點 |
任意位置添加刪除元素的都非常的快 |
缺點 |
查找方便嘹朗,不適合隨機查找 |
雙鏈表 |
結(jié)點即要保存到下一個元素的指針,還要保存一個上一個元素的指針 |
typedef struct listNode {
// 前置節(jié)點
struct listNode *prev;
// 后置節(jié)點
struct listNode *next;
// 節(jié)點的值
void *value;
} listNode;
壓縮列表 |
描述 |
ziplist |
壓縮列表是列表诵肛、哈希的底層實現(xiàn)之一屹培,壓縮列表可以包含多個節(jié)點,每個節(jié)點保存一個字節(jié)數(shù)組或者整數(shù)值 |
壓縮列表節(jié)點 |
描述 |
previous_entry_length |
記錄了壓縮列表中前一個節(jié)點的長度 |
encoding |
數(shù)據(jù)類型以及長度 |
content |
節(jié)點值 |
List |
總結(jié) |
list |
list = 壓縮列表 或者 雙鏈表怔檩,雙鏈表廣泛運用在List/發(fā)布與訂閱/慢查詢/監(jiān)控器等 |
/*
* 字典
*/
typedef struct dict {
dictType *type;
void *privdata;
// 哈希表褪秀,每個字典包含兩個table
//一般情況下字典只使用ht[0]這個哈希表,ht[1]只會在對哈希表進行rehash時才會使用
dictht ht[2];
int rehashidx;
int iterators;
} dict;
typedef struct dictht {
dictEntry **table; // 哈希表數(shù)組 ,存儲dictEntry
unsigned long size;
unsigned long sizemask;
unsigned long used;
} dictht;
/*
* hash表節(jié)點
*/
typedef struct dictEntry {
void *key; // 鍵
union { // 值
void *val;
uint64_t u64;
int64_t s64;
} v;
struct dictEntry *next; // 指向下個哈希表節(jié)點薛训,形成鏈表
} dictEntry
hash |
描述 |
哈希算法 |
當(dāng)要將一個新的鍵值對添加到字典里面時媒吗,程序會根據(jù)鍵計算出哈希值和索引值,然后再根據(jù)索引值將新鍵值對放到哈希表數(shù)組指定的索引上 |
鍵沖突 |
兩個不同的鍵值對(key相同覆蓋乙埃,不同放入鏈表)闸英,計算出的哈希值和索引值是一樣的锯岖,使用鏈地址法:每個hash節(jié)點都有一個next指針,指向下一個元素存儲位置自阱,形成一個單鏈表(取值時直接比較key是否相等) |
字典 |
每個字典包含兩張哈希表數(shù)組,一般情況下字典只使用ht[0]這個哈希表嚎莉,ht[1]只會在對哈希表進行rehash時才會使用 |
rehash |
哈希表擴展或者收縮時米酬,將現(xiàn)有哈希表的所有鍵值對rehash到另外一張哈希表沛豌。但是這個rehash動作并不是一次性完成的,而是分多次漸進式完成的(鍵值對過多時赃额,一次性rehash肯定會消耗服務(wù)器大量的計算資源) |
/*
* 整數(shù)集合
*/
typedef struct intset {
uint32_t encoding;
uint32_t length;
int8_t contents[]; // 保存元素的數(shù)組
} intse
無序集合 |
描述 |
set |
使用了intset和hashtable兩種數(shù)據(jù)結(jié)構(gòu)存儲的 |
整數(shù)集合 |
intset存儲條件:①結(jié)合對象保存的所有元素都是整數(shù)值②集合對象保存的元素數(shù)量不超過512個; 整數(shù)集合數(shù)組元素從小到大有序地排列加派,并且數(shù)組中不包含任何重復(fù)項 |
跳躍表性質(zhì) |
由很多層結(jié)構(gòu)組成 |
每一層都是一個有序的鏈表 |
最底層(Level 1)的鏈表包含所有元素 |
如果一個元素出現(xiàn)在 Level i 的鏈表中,則它在 Level i 之下的鏈表也都會出現(xiàn) |
每個節(jié)點包含兩個指針跳芳,一個指向同一鏈表中的下一個元素芍锦,一個指向下面一層的元素 |
typedef struct zskiplistNode {
//層
struct zskiplistLevel {
struct zskiplistNode *forward;//前進指針
unsigned int span;//跨度
} level[];
struct zskiplistNode *backward; //后退指針
double score; //分值
robj *obj; //成員對象
} zskiplistNode;
typedef struct zskiplist {
struct zskiplistNode *header, *tail; //表頭節(jié)點和表尾節(jié)點
unsigned long length; //表中節(jié)點的的數(shù)量
int level; //表中層數(shù)最大的節(jié)點層數(shù)
} zskiplist;
內(nèi)存回收
typedef struct redisObject {
// ...
// 引用計數(shù)
int refcount;
// ...
} robj;
內(nèi)存回收 |
Redis 構(gòu)建了一個由引用計數(shù)實現(xiàn)的內(nèi)存回收機制,通過這一機制飞盆,程序可以通過跟蹤對象的引用技術(shù)信息娄琉,在適當(dāng)?shù)臅r候自動釋放對象并進行內(nèi)存回收 |
對象共享
對象共享 |
Redis 在初始化服務(wù)器時,會創(chuàng)建一萬個字符串對象吓歇,這些對象包含了 0 到 9999 的所有整數(shù)值孽水。當(dāng)服務(wù)器需要用到值為 0 到 9999 的字符串對象時,服務(wù)器就會使用這些共享對象城看。此外每被一個建引用對象的引用計數(shù)加一 |
redis數(shù)據(jù)庫
redis數(shù)據(jù)庫 |
描述 |
redisServer |
Redis是一個字典結(jié)構(gòu)的存儲服務(wù)器女气,默認支持16個數(shù)據(jù)庫 |
redisClient |
可以指向不同數(shù)據(jù)庫,達到切換目標數(shù)據(jù)庫的目的 |
redisDb |
dict保存所有的鍵值對测柠,每一個建都是字符串炼鞠,每一個值都可以是任意Redis基本類型 |
struct redisServer {
redisDb *db; //一個數(shù)組,保存著服務(wù)器中的所有數(shù)據(jù)庫
int dbnum; //根據(jù)dbnum屬性創(chuàng)建服務(wù)器的數(shù)據(jù)庫數(shù)量,默認16
};
typedef struct redisClient {
redisDb *db; //這個屬性指向了redisDb數(shù)組的其中一個元素
} redisClient;
typedef struct redisDb {
dict *dict; //屬于redisDb *db數(shù)組結(jié)構(gòu)轰胁, 保存所有的鍵值對
} redisDb;
建過期
建過期 |
描述 |
expires |
過期字典谒主,鍵指向dict的某一個鍵值對,值保存過期時間 |
過期時間查詢 |
過期時間減去當(dāng)前時間得到鍵剩余時間:TTL(秒為單位返回鍵的剩余生存時間) /PTTL(毫秒為單位返回鍵的剩余生存時間) |
過期鍵判斷 |
當(dāng)前時間戳跟過期時間戳比較赃阀, 當(dāng)前時間戳大于過期時間戳代表過期 |
typedef struct redisDb {
dict *expires; //過期字典瘩将,保存鍵過期時間
} redisDb;
過期建刪除
刪除策略 |
描述 |
定時刪除 |
在設(shè)置鍵的過期時間的同時,創(chuàng)建一個定時器(timer)凹耙,讓定時器在鍵的過期時間來臨時姿现,立即執(zhí)行對鍵的刪除操作 |
惰性刪除(對于CPU是最友好的) |
放任鍵過期不管,但是每次從鍵空間中獲取鍵時肖抱,都檢查取得的鍵是否過期备典,如果過期的話,就刪除該鍵意述;如果沒有過期提佣,就返回該鍵 |
定期刪除 |
每隔一段時間吮蛹,程序就對數(shù)據(jù)庫進行一次檢查,刪除里面的過期鍵拌屏。至于要刪除多少過期鍵潮针,以及要檢查多少個數(shù)據(jù)庫,則由算法決定 |
RDB/AOF過期鍵處理
過期鍵處理 |
描述 |
生成RDB |
執(zhí)行SAVE命令或者BGSAVE命令所產(chǎn)生的新RDB文件不會包含已經(jīng)過期的鍵 |
載入RDB |
服務(wù)器開啟了RDB 功能的情況倚喂,主服務(wù)器啟動時每篷,會檢查所有鍵并忽略過期鍵,從服務(wù)器啟動時無論是否過期都會載入RDB(主從同步時端圈,從庫RDB會被清空) |
重寫AOF |
BGREWRITEAOF:重寫AOF文件不會包含已經(jīng)過期的鍵 |
AOF持久化運行模式 |
當(dāng)一個鍵被惰性或者定期刪除焦读,服務(wù)器會追加一條DEL命令到現(xiàn)有AOF文件的末尾,顯式地刪除過期鍵 |
RDB
RDB |
描述 |
RDB |
RDB用于保存和還原所有字典的鍵值對 |
SAVE |
由服務(wù)器進程直接操作舱权,阻塞其他命令 |
BGSAVE |
服務(wù)器子進程操作矗晃,不會阻塞 |
自動間隔保存 |
redis容許用戶手動配置save選項,當(dāng)滿足條件時宴倍,自動觸發(fā)BGSAVE 命令 |
dirty計數(shù)器 |
后數(shù)據(jù)庫每修改一次dirty屬性自增1,執(zhí)行save或者bgsave操作命令之后清空零 |
lastsave |
記錄上一次成功執(zhí)行save或者bgsave操作命令的時間 |
檢查保存條件是否滿足 |
周期性檢查dirty屬性和lastsave屬性是否滿足了savparam條件张症,滿足就執(zhí)行BGSAVE |
struct redisServer{
struct saveparam *saveparams; //記錄保存條件的數(shù)組
};
/** redis容許用戶手動配置save選項,當(dāng)滿足條件時鸵贬,自動觸發(fā)BGSAVE 命令 */
struct savparam{
time_t seconds; //秒數(shù)
int changes; //修改數(shù)
};
struct redisServer{
long long dirty; //修改計數(shù)器
time_t lastsave; //上一次執(zhí)行保存操作的時間
};
AOF
AOF |
描述 |
AOF |
保存Redis服務(wù)器所執(zhí)行的寫命令來記錄數(shù)據(jù)庫狀態(tài)的 |
命令追加 |
服務(wù)器在執(zhí)行完一個寫入命令之后俗他,會以協(xié)議格式將被執(zhí)行的寫入命令追加到服務(wù)器狀態(tài)的aof_buf緩存區(qū)的末尾(如:set 命令) |
文件寫入與同步 |
服務(wù)器每次結(jié)束一個事件循環(huán)之前, 它都會調(diào)用flushAppendOnlyFile函 數(shù)恭理, 考慮是否需要將aof_buf緩沖區(qū)中的內(nèi)容寫人和保存到AOF文件里面 |
文件載入與還原 |
因為AOF文件里面包含了重建數(shù)據(jù)庫狀態(tài)所需的所有寫命令拯辙,所以服務(wù)器只要讀人并重新執(zhí)行一遍AOF文件里面保存的寫命令, 就可以還原服務(wù)器關(guān)閉之前的數(shù)據(jù)庫狀態(tài) |
struct redisServer{
sds aof_buf; // aof 緩存區(qū)
}:
事件
事件 |
描述 |
事件驅(qū)動 |
Redis服務(wù)器是一個事件驅(qū)動器颜价,服務(wù)器需要處理以下兩類事件:文件事件與時間事件 |
文件事件 |
通過I/O多路復(fù)用程序來同時監(jiān)聽多個套接字對應(yīng)分配處理事件 |
文件事件原理 |
基于Reactor模式 |
時間事件 |
Redis時間事件分為兩大類:定時事件與周期性事件 |
時間事件原理 |
Redis服務(wù)器會將所有的時間事件放入到一個鏈表涯保,每當(dāng)時間事件執(zhí)行器運行時,它就遍歷整個鏈表周伦,查找所有已經(jīng)到達的時間事件,并調(diào)用相應(yīng)的事件處理器 |
在3.0的版本中,服務(wù)器只有一個serverCron的時間事件
1.更新服務(wù)器的各類統(tǒng)計信息速侈,比如時間倚搬、內(nèi)存占用,數(shù)據(jù)庫占用情況等;
2.清理數(shù)據(jù)庫中過期的鍵值空間馒闷;
3.關(guān)閉和清理實現(xiàn)的客戶端連接航揉;
4.嘗試進行AOF或RDB持久化操作尤蛮;
5.如果是主服務(wù)器,會定期的對從服務(wù)器進行同步;
6.如果處于集群模式,對集群定期的進行同步和連接測試
Redis單線程
Redis單線程 |
描述 |
Redis服務(wù)器 |
服務(wù)器狀態(tài)結(jié)構(gòu)使用clients鏈表連接多個客戶端狀態(tài),新添加的客戶端狀態(tài)會被放到鏈表末尾 |
struct redisServer {
list *clients; //一個鏈表,保存所有的客戶端狀態(tài)
};
Redis是單線程的,為何不利用多個CPU ?
官方解釋:CPU通常Redis的瓶頸,瓶頸一般是內(nèi)存或網(wǎng)絡(luò)IO。
訂閱頻道
訂閱頻道 |
描述 |
SUBSCRIBE |
訂閱頻道,將客戶端添加到鏈表的末尾 |
PUBLISH |
發(fā)送消息到頻道 |
UNSUBSCRIBE |
它從 pubsub_channels 字典的給定頻道(鍵)中乃沙, 刪除關(guān)于當(dāng)前客戶端的信息 |
struct redisServer {
dict *pubsub_channels; //鍵代表被訂閱的頻道, 值代表鏈表,保存了所有訂閱這個頻道的客戶端
};
def SUBSCRIBE(client, channels): //偽代碼
#遍歷所有輸入頻道
for channel in channels:
# 將客戶端添加到鏈表的末尾
redisServer.pubsub_channels[channel].append(client)
def PUBLISH(channel, message): //偽代碼
# 遍歷所有訂閱頻道 channel 的客戶端
for client in server.pubsub_channels[channel]:
# 將信息發(fā)送給它們
send_message(client, message)
訂閱評定與訂閱模式
客戶端A發(fā)送消息給news.it頻道,客戶端C、客戶端D同樣受到消息(PUBLISH 命令發(fā)送信息到某個頻道時,訂閱頻道客戶端 與 匹配訂閱模式的的客戶端同樣受到信息)
訂閱模式
訂閱模式 |
描述 |
SUBSCRIBE |
訂閱模式 |
PUNSUBSCRIBE |
程序會刪除 redisServer.pubsub_patterns 鏈表中, 所有和被退訂模式相關(guān)聯(lián)的 pubsubPattern 結(jié)構(gòu) |
struct redisServer {
list *pubsub_patterns; //一個鏈表
};
typedef struct pubsubPattern {
redisClient *client; //訂閱模式的客戶端
robj *pattern; //訂閱的模式
} pubsubPattern;
def PUBLISH(channel, message): //完整的PUBLISH偽代碼
# 遍歷所有訂閱頻道 channel 的客戶端
for client in server.pubsub_channels[channel]:
# 將信息發(fā)送給它們
send_message(client, message)
# 取出所有模式托嚣,以及訂閱模式的客戶端
for pattern, client in server.pubsub_patterns:
# 如果 channel 和模式匹配
if match(channel, pattern):
# 那么也將信息發(fā)給訂閱這個模式的客戶端
send_message(client, message)
事物
事物 |
描述 |
事物概念 |
事務(wù)提供了一種“將多個命令打包示启, 然后一次性、按順序地執(zhí)行”的機制郑诺, 并且事務(wù)在執(zhí)行的期間不會主動中斷 —— 服務(wù)器在執(zhí)行完事務(wù)中的所有命令之后轻抱, 才會繼續(xù)處理其他客戶端的其他命令 |
MULTI |
讓客戶端從非事務(wù)狀態(tài)切換到事務(wù)狀態(tài),將這些命令全部放進一個事務(wù)隊列 |
事務(wù)隊列 |
一個數(shù)組(參數(shù):執(zhí)行的命令,命令的參數(shù)容燕,參數(shù)的個數(shù)) |
EXEC |
服務(wù)器根據(jù)客戶端所保存的事務(wù)隊列梁呈, 以先進先出(FIFO)的方式執(zhí)行事務(wù)隊列中的命令 |
DISCARD |
取消一個事務(wù), 清空客戶端的整個事務(wù)隊列 |
帶 WATCH 的事務(wù) |
事務(wù)開始之前監(jiān)視任意數(shù)量的鍵: 當(dāng)調(diào)用 EXEC 命令執(zhí)行事務(wù)時蘸秘, 如果任意一個被監(jiān)視的鍵已經(jīng)被其他客戶端修改了官卡, 那么整個事務(wù)不再執(zhí)行, 直接返回失敗 |
watched_keys |
鍵被監(jiān)視的鍵醋虏, 值則是一個鏈表寻咒, 鏈表中保存了所有監(jiān)視這個鍵的客戶端 |
WATCH 原理 |
將當(dāng)前客戶端和要監(jiān)視的鍵在 watched_keys 中進行關(guān)聯(lián) |
WATCH 觸發(fā) |
在任何對數(shù)據(jù)庫鍵空間進行修改的命令成功執(zhí)行之后,檢查數(shù)據(jù)庫 watched_keys 字典, 查找被修改的客戶端并打開REDIS_DIRTY_CAS 標識 |
WATCH 事物下 執(zhí)行 EXEC 命令 |
REDIS_DIRTY_CAS 標識被打開代表事務(wù)的安全性已經(jīng)被破壞颈嚼,清空清空事務(wù)隊列并返回 |
def execute_transaction(): //事務(wù)的整個執(zhí)行過程偽代碼
# 創(chuàng)建空白的回復(fù)隊列
reply_queue = []
# 取出事務(wù)隊列里的所有命令毛秘、參數(shù)和參數(shù)數(shù)量
for cmd, argv, argc in client.transaction_queue:
# 執(zhí)行命令,并取得命令的返回值
reply = execute_redis_command(cmd, argv, argc)
# 將返回值追加到回復(fù)隊列末尾
reply_queue.append(reply)
# 清除客戶端的事務(wù)狀態(tài)
clear_transaction_state(client)
# 清空事務(wù)隊列
clear_transaction_queue(client)
# 將事務(wù)的執(zhí)行結(jié)果返回給客戶端
send_reply_to_client(client, reply_queue)
typedef struct redisDb {
dict *watched_keys; //鍵被監(jiān)視的鍵, 值則是一個鏈表叫挟, 鏈表中保存了所有監(jiān)視這個鍵的客戶端
} redisDb;
事物
事務(wù)的 ACID 性質(zhì) |
描述 |
原子性(Atomicity) |
單個 Redis 命令的執(zhí)行是原子性的艰匙,但 Redis 事務(wù)的執(zhí)行并不是原子性的,當(dāng)事務(wù)失敗時,Redis 也不會進行任何的重試或者回滾動作 |
一致性(Consistency) |
Redis的事務(wù)并不具有一致性抹恳,①入隊錯誤(發(fā)送錯誤命令旬薯,EXEC 命令時返回拒絕執(zhí)行)②執(zhí)行錯誤(執(zhí)行過程錯誤,也不會影響后面要執(zhí)行的事務(wù)命令)③Redis 進程被終結(jié)( Redis 服務(wù)器進程在執(zhí)行事務(wù)的過程中被其他進程終結(jié)适秩,或者被管理員強制殺死且沒有采取任何持久化機制绊序,重啟之后的數(shù)據(jù)庫總是空白的) |
隔離性(Isolation) |
Redis 是單進程程序,并且它保證在執(zhí)行事務(wù)時秽荞,不會對事務(wù)進行中斷骤公,事務(wù)可以運行直到執(zhí)行完所有事務(wù)隊列中的命令為止。因此扬跋,Redis 的事務(wù)是總是帶有隔離性的 |
持久性(Durability) |
事務(wù)的持久性由 Redis 所使用的持久化模式?jīng)Q定 |
復(fù)制
復(fù)制 |
描述 |
復(fù)制 |
通過執(zhí)行SLAVEOF命令或設(shè)置slaveof選項阶捆,可以讓一個服務(wù)器去復(fù)制另一個服務(wù)器 |
舊版復(fù)制
舊版復(fù)制 |
描述 |
同步(sync) |
同步操作用于將從服務(wù)器的數(shù)據(jù)庫狀態(tài)更新至主服務(wù)器當(dāng)前所處的數(shù)據(jù)庫狀態(tài) |
命令傳播(command propagate) |
命令傳播操作則用于在主服務(wù)器的數(shù)據(jù)庫狀態(tài)被修改,導(dǎo)致主從服務(wù)器的數(shù)據(jù)庫狀態(tài)出現(xiàn)不一致時钦听,主服務(wù)器會將自己執(zhí)行的寫命令洒试,發(fā)送給從服務(wù)器執(zhí)行 |
同步過程
①從服務(wù)器向主服務(wù)器發(fā)送SYNC命令;
②收到SYNC命令的主服務(wù)器執(zhí)行BGSAVE命令朴上,在后臺生成一個RDB文件垒棋,并使用一個緩沖區(qū)記錄從現(xiàn)在開始執(zhí)行的所有寫命令;
③當(dāng)主服務(wù)器的BGSAVE命令執(zhí)行完畢時痪宰,主服務(wù)器會將BGSAVE命令生成的RDB文件發(fā)送給從服務(wù)器叼架,從服務(wù)器接收并載入這個RDB文件,將自己的數(shù)據(jù)庫狀態(tài)更新至主服務(wù)器執(zhí)行BGSAVE命令時的數(shù)據(jù)庫狀態(tài)衣撬。
④主服務(wù)器將記錄在緩沖區(qū)里面的所有寫命令發(fā)送給從服務(wù)器乖订,從服務(wù)器執(zhí)行這些寫命令,將自己的數(shù)據(jù)庫狀態(tài)更新至主服務(wù)器數(shù)據(jù)庫當(dāng)前所處的狀態(tài)具练。
新版復(fù)制
新版復(fù)制 |
描述 |
PSYNC |
解決舊版在處理重復(fù)制的低效率問題乍构,分為完整重同步和部分重同步,分別用于處理初次復(fù)制(與SYNC命令一樣)和斷線后重復(fù)制 |
部分重同步
部分重同步 |
描述 |
部分重同步 |
分為三個部分:①主從服務(wù)器的復(fù)制偏移量②主服務(wù)器復(fù)制積壓緩沖區(qū)③服務(wù)器運行ID |
主從服務(wù)器的復(fù)制偏移量 |
主從服務(wù)器會分別維護一個復(fù)制偏移量,通過對比偏移量扛点,可以知道主從服務(wù)器是否處于一致狀態(tài),不一致則執(zhí)行部分重同步 |
偏移量 |
主服務(wù)器每次向從服務(wù)器傳播N個字節(jié)的數(shù)據(jù)時哥遮,就將自己的復(fù)制偏移量的值加上N;從服務(wù)器每次收到主服務(wù)器傳播來的N個字節(jié)的數(shù)據(jù)時占键,就將自己的復(fù)制偏移量的值加上N昔善; |
主服務(wù)器復(fù)制積壓緩沖區(qū) |
主服務(wù)器維護一個固定長度先進先出的隊列,即為復(fù)制積壓緩沖區(qū),保存一部分最近傳播的寫命令畔乙,且為每個字節(jié)記錄相應(yīng)的復(fù)制偏移量 |
服務(wù)器運行ID |
當(dāng)斷線重連時君仆,用來判斷從服務(wù)器與上次連接的是否為同一主服務(wù)器 |
sentinel
sentinel |
描述 |
sentinel |
redis主從復(fù)制可將主節(jié)點數(shù)據(jù)同步給從節(jié)點,一旦主節(jié)點宕機,從節(jié)點作為主節(jié)點的備份可以隨時頂上來 |
啟動并初始化sentinel |
初始化sentinel分別為5個步驟 |
①初始化服務(wù)器 |
Sentinel的本質(zhì)是一個運行在特殊模式下的Redis服務(wù)器返咱,不需要使用數(shù)據(jù)庫钥庇,因此不載入RDB或者AOF |
②將普通 Redis 服務(wù)器使用的代碼替換成 Sentinel 專用代碼 |
將普通Redis服務(wù)器使用的代碼替換成Sentinel專用的代碼 ,命令表中部分命令不載入咖摹,所以sentinel 部分命令無法使用(如:鍵值對命令评姨,持久化命令,事物命令萤晴,腳本命令) |
③初始化 Sentinel 狀態(tài) |
保存了服務(wù)器所有和Sentinel功能有關(guān)的狀態(tài) |
④根據(jù)給定的配置文件吐句, 初始化 Sentinel 的監(jiān)視主服務(wù)器列表 |
將sentinel狀態(tài)的 masters 屬性進行初始化,masters 里面保存的是所有被監(jiān)視的主服務(wù)器的信息以及其鍵值對應(yīng)保存的是什么內(nèi)容 |
⑤創(chuàng)建連向主服務(wù)器的網(wǎng)絡(luò)連接 |
創(chuàng)建連向被監(jiān)視主服務(wù)器的網(wǎng)絡(luò)連接店读,Sentinel將成為主服務(wù)器的客戶端嗦枢,可以向主服務(wù)器發(fā)送命令,并從命令回復(fù)中獲取相關(guān)的信息 (兩個連向主服務(wù)器的異步網(wǎng)絡(luò)連接:命令連接屯断,用于向主服務(wù)器發(fā)送命令文虏,并接收命令回復(fù),訂閱連接殖演,用于訂閱主服務(wù)器的 sentinel:hello 頻道) |
獲取主服務(wù)器信息 |
Sentinel 默認每十秒一次氧秘,向監(jiān)視的主服務(wù)器發(fā)送 INFO 命令,并分析 INFO 命令的回復(fù)來 獲取主服務(wù)器當(dāng)前信息 以及 主服務(wù)器屬下的所有從服務(wù)器信息 |
獲取從服務(wù)器信息 |
當(dāng)Sentinel發(fā)現(xiàn)主服務(wù)器有新的服務(wù)器出現(xiàn)時趴久,除了會為這個新從服務(wù)器創(chuàng)建相應(yīng)的實例結(jié)構(gòu)之外丸相,還會創(chuàng)建連接到從服務(wù)器的命令連接和訂閱連接 |
Sentinel 發(fā)送與接收 |
在建立起訂閱連接之后 ,通過頻道向主服務(wù)器和從服務(wù)器發(fā)送和接收信息 |
檢測主觀下線 |
發(fā)送 PING 命令,并通過實例返回的 PING 命令回復(fù)來判斷實例是否在線(指的是單個 Sentinel 實例對服務(wù)器做出的下線判斷) |
檢測客觀下線 |
當(dāng)sentinel監(jiān)視的某個服務(wù)主觀下線后朋鞍,sentinel會詢問其它監(jiān)視該服務(wù)的sentinel已添,看它們是否也認為該服務(wù)主觀下線(超過半數(shù)就為主觀下線,指的是多個 Sentinel 實例在對同一個服務(wù)器做出 SDOWN 判斷) |
選舉領(lǐng)頭sentinel |
當(dāng)一個主服務(wù)器被判斷為客觀下線時滥酥,監(jiān)視這個下線主服務(wù)器的各個Sentinel會進行協(xié)商,選舉出一個領(lǐng)頭Sentinel畦幢,并由領(lǐng)頭Sentinel對下線主服務(wù)器進行故障轉(zhuǎn)移操作 |
故障轉(zhuǎn)移 |
領(lǐng)頭Sentinel挑選一個狀態(tài)良好坎吻、數(shù)據(jù)完整的從服務(wù)器,然后發(fā)送 SLAVEOF no one 命令宇葱,然后將這個從服務(wù)器轉(zhuǎn)換成主服務(wù)器 |
集群
集群 |
描述 |
集群 |
Redis集群是分布式數(shù)據(jù)庫方案瘦真,通過分片來進行數(shù)據(jù)共享,并提供復(fù)制和故障轉(zhuǎn)移功能 |
節(jié)點 |
一個Redis 集群通常由多個節(jié)點組成黍瞧,每個節(jié)點都是獨立的,想要構(gòu)建成一個包含多個節(jié)點的集群诸尽,可以向一個節(jié)點發(fā)送 CLUSTER MEET <ip> <port>命令,可以讓節(jié)點與ip和port所指定的節(jié)點進行握手印颤,握手成功節(jié)點就會將ip和port指定的節(jié)點添加到當(dāng)前的集群中 |
啟動節(jié)點 |
節(jié)點的本質(zhì)還是服務(wù)器您机,服務(wù)器會根據(jù) cluster-enabled(redis.conf配置項) 配置選項來決定是否開啟集群模式 |
槽 |
Redis集群使用分片方式保存鍵值對,整個集群分為16384 個槽,每個鍵值對都屬于槽的其中一個 |
槽指派 |
集群槽可以指派給各個節(jié)點际看, 當(dāng)數(shù)據(jù)庫中的 16384 個槽都有節(jié)點在處理時咸产,集群處于上線狀態(tài),否則仲闽,處于下線狀態(tài) |
集群中的執(zhí)行命令 |
數(shù)據(jù)庫向節(jié)點發(fā)送鍵命令脑溢,會判斷槽是否由當(dāng)前節(jié)點,如果當(dāng)節(jié)點發(fā)現(xiàn)鍵所在的槽并非由自己負責(zé)處理的時候赖欣,就會向客戶端返回一個 MOVED 錯誤屑彻,指引客戶端轉(zhuǎn)向正在負責(zé)槽的節(jié)點 ,如果是則進行執(zhí)行 |
重新分片 |
Redis集群的重新分片操作可以將任意數(shù)量已經(jīng)指派給原本節(jié)點的槽 改為指派給目標節(jié)點顶吮,并且相關(guān)槽所屬的鍵值對也會從源節(jié)點移動到目標節(jié)點 |
ASK 錯誤 |
在重新分片期間酱酬,源節(jié)點向目標節(jié)點遷移一個槽的過程中,可能會出現(xiàn)這樣一種情況:屬于被遷移槽的一部分鍵值對保存在源節(jié)點里面云矫,而另一部分鍵值對保存在目標節(jié)點中 ------ 當(dāng)客戶端向源節(jié)點發(fā)送關(guān)于鍵key的命令膳沽,源節(jié)點先在自己的數(shù)據(jù)庫里查找這個鍵,如果找到就直接返回執(zhí)行客戶端命令让禀,如果沒找到挑社,這個鍵可能已經(jīng)被遷移到了目標節(jié)點,源節(jié)點向客戶端返回一個 ASK 錯誤巡揍,指引客戶端轉(zhuǎn)向正在導(dǎo)入槽的目標節(jié)點痛阻,并再次發(fā)送之前要執(zhí)行的命令 |
復(fù)制與故障轉(zhuǎn)移 |
Redis集群分為主節(jié)點與從節(jié)點,主節(jié)點處理槽腮敌,從節(jié)點負責(zé)復(fù)制某節(jié)點 |
故障檢測 |
集群中的每個節(jié)點都會定期地想集群中的其他節(jié)點發(fā)送 PING 消息阱当,以此來檢測對方是否在線 |
故障轉(zhuǎn)移 |
當(dāng)一個從節(jié)點發(fā)現(xiàn)自己正在復(fù)制的主節(jié)點進入了已下線狀態(tài),從節(jié)點將開始對下線主節(jié)點進行故障轉(zhuǎn)移:被選中的從節(jié)點將執(zhí)行 slaveof no one 命令糜工,成為新的主節(jié)點弊添,并將這些槽全部指派給自己,新的主節(jié)點向集群廣播一條PONG消息捌木,告訴集群中的其他節(jié)點自己成為了新的主節(jié)點 |
redis常見問題
(一)緩存和數(shù)據(jù)庫雙寫一致性問題
(二)緩存雪崩問題
(三)緩存擊穿問題
(四)緩存的并發(fā)競爭問題