上一篇文章介紹了redis基本的數據結構和對象《redis設計與實現》1-數據結構與對象篇node
本文主要關於:linux
所在文件爲server.h。數據庫中全部針對鍵值對的增刪改查,都是對dict作操做git
typedef struct redisDb {
dict *dict; /* The keyspace for this DB */
dict *expires; /* Timeout of keys with a timeout set */
dict *blocking_keys; /* Keys with clients waiting for data (BLPOP)*/
dict *ready_keys; /* Blocked keys that received a PUSH */
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
int id; /* Database ID */
long long avg_ttl; /* Average TTL, just for stats */
} redisDb;
複製代碼
讀寫鍵空間時,是針對dict作操做,可是除了完成基本的增改查找操做,還會執行一些額外的維護操做,包括:github
相關命令:info stats keyspace_hits, info stats keyspace_missesredis
過時時間保存在expires的字典中,值爲long類型的毫秒時間戳算法
策略類型 | 描述 | 優勢 | 缺點 | redis是否採用 |
---|---|---|---|---|
定時刪除 | 經過定時器實現 | 保證過時鍵能儘快釋放 | 對cpu不友好,影響相應時間和吞吐量 | 否 |
惰性刪除 | 聽任無論,查詢時纔去檢查 | 對cpu友好 | 沒有被訪問的永遠不會被釋放,至關於內存泄露 | 是 |
按期刪除 | 每隔一段時間檢查 | 綜合前面的優勢 | 難於肯定執行時長和頻率 | 是 |
redis採用了惰性刪除和按期刪除策略docker
subscribe __keyspace@0__:keyname
複製代碼
subscribe __keyevent@0__:del
複製代碼
生成rdb文件的兩個命令以下,實現函數爲rdb.c文件的rdbSave函數:數據庫
RDB文件的載入是在服務器啓動時自動執行的,實現函數爲rdb.c文件的rdbload函數。載入期間服務器一直處於阻塞狀態編程
redis容許用戶經過設置服務器配置的server選項,讓服務器每隔一段時間(100ms)自動執行BGSAVE命令(serverCron函數)json
//server.c中main函數內部建立定時器,serverCron爲定時任務回調函數
if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
serverPanic("Can't create event loop timers.");
exit(1);
}
複製代碼
// 任意一個配置知足即執行
save 900 1 // 900s內,對服務器進行至少1次修改
save 300 10 // 300s內,對服務器至少修改10次
複製代碼
// 服務器全局變量,前面介紹過
struct redisServer {
...
/* RDB persistence */
// 上一次執行save或bgsave後,對數據庫進行了多少次修改
long long dirty; /* Changes to DB from the last save */
long long dirty_before_bgsave; /* Used to restore dirty on failed BGSAVE */
pid_t rdb_child_pid; /* PID of RDB saving child */
struct saveparam *saveparams; /* Save points array for RDB */
int saveparamslen; /* Number of saving points */
char *rdb_filename; /* Name of RDB file */
int rdb_compression; /* Use compression in RDB? */
int rdb_checksum; /* Use RDB checksum? */
// 上一次成功執行save或bgsave的時間
time_t lastsave; /* Unix time of last successful save */
time_t lastbgsave_try; /* Unix time of last attempted bgsave */
time_t rdb_save_time_last; /* Time used by last RDB save run. */
time_t rdb_save_time_start; /* Current RDB save start time. */
int rdb_bgsave_scheduled; /* BGSAVE when possible if true. */
int rdb_child_type; /* Type of save by active child. */
int lastbgsave_status; /* C_OK or C_ERR */
int stop_writes_on_bgsave_err; /* Don't allow writes if can't BGSAVE */
int rdb_pipe_write_result_to_parent; /* RDB pipes used to return the state */
int rdb_pipe_read_result_from_child; /* of each slave in diskless SYNC. */
...
};
// 具體每個參數對應的變量
struct saveparam {
time_t seconds;
int changes;
};
複製代碼
type爲value的類型,1字節,表明對象類型或底層編碼,根據type決定如何讀取value
每一個value保存一個值對象,與type對應。type不一樣,value的結構,長度也有所不一樣
使用linux自帶的od命令能夠查看rdb文件信息,好比od -c dump.rdb,以Ascii打印,下圖顯示docker建立的redis中,空的rdb文件輸出的內容
除了RDB持久化外,redis還提供了AOF持久化功能。區別以下:
AOF持久化分爲三步:
事件結束時調用flushAppendOnlyFile函數,考慮是否將aof_buf內容寫到AOF文件裏(參數決定)
服務器只須要讀入並執行一遍AOF命令便可還原數據庫狀態,讀取的步驟以下:
redis是一個事件驅動程序,事件包括兩大類:
可選的io多路複用包括select,epoll,evport,kqueue實現。每種實現都放在單獨的文件中。編譯時根據不一樣的宏切換不一樣的實現
事件類型
#define AE_NONE 0 /* No events registered. */
#define AE_READABLE 1 /* Fire when descriptor is readable. */
#define AE_WRITABLE 2 /* Fire when descriptor is writable. */
複製代碼
redis爲文件事件編寫了多個處理器,分別用於實現不一樣的網絡需求,在networking.c文件中,包括:
時間事件分類如下兩大類,取決於時間處理器的返回值:
時間事件包括三個屬性:
int main() {
...
aeMain(server.el);
...
}
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
}
}
複製代碼
redis服務器爲每一個鏈接的客戶端創建了一個redisClient的結構,保存客戶端狀態信息。全部客戶端的信息放在一個鏈表裏。可經過client list命令查看
struct redisServer {
...
list *clients;
...
}
複製代碼
客戶端數據結構以下:
typedef struct client {
uint64_t id; /* Client incremental unique ID. */
//客戶端套接字描述符,僞客戶端該值爲-1(包括AOF還原和執行Lua腳本的命令)
int fd; /* Client socket. */
redisDb *db; /* Pointer to currently SELECTed DB. */
// 客戶端名字,默認爲空,可經過client setname設置
robj *name; /* As set by CLIENT SETNAME. */
// 輸入緩衝區,保存客戶端發送的命令請求,不能超過1G
sds querybuf; /* Buffer we use to accumulate client queries. */
size_t qb_pos; /* The position we have read in querybuf. */
sds pending_querybuf; /* If this client is flagged as master, this buffer represents the yet not applied portion of the replication stream that we are receiving from the master. */
size_t querybuf_peak; /* Recent (100ms or more) peak of querybuf size. */
// 解析querybuf,獲得參數個數
int argc; /* Num of arguments of current command. */
// 解析querybuf,獲得參數值
robj **argv; /* Arguments of current command. */
// 根據前面的argv[0], 找到這個命令對應的處理函數
struct redisCommand *cmd, *lastcmd; /* Last command executed. */
int reqtype; /* Request protocol type: PROTO_REQ_* */
int multibulklen; /* Number of multi bulk arguments left to read. */
long bulklen; /* Length of bulk argument in multi bulk request. */
// 服務器返回給客戶端的可被空間,固定buff用完時纔會使用
list *reply; /* List of reply objects to send to the client. */
unsigned long long reply_bytes; /* Tot bytes of objects in reply list. */
size_t sentlen; /* Amount of bytes already sent in the current buffer or object being sent. */
// 客戶端的建立時間
time_t ctime; /* Client creation time. */
// 客戶端與服務器最後一次互動的時間
time_t lastinteraction; /* Time of the last interaction, used for timeout */
// 客戶端空轉時間
time_t obuf_soft_limit_reached_time;
// 客戶端角色和狀態:REDIS_MASTER, REDIS_SLAVE, REDIS_LUA_CLIENT等
int flags; /* Client flags: CLIENT_* macros. */
// 客戶端是否經過身份驗證的標識
int authenticated; /* When requirepass is non-NULL. */
int replstate; /* Replication state if this is a slave. */
int repl_put_online_on_ack; /* Install slave write handler on ACK. */
int repldbfd; /* Replication DB file descriptor. */
off_t repldboff; /* Replication DB file offset. */
off_t repldbsize; /* Replication DB file size. */
sds replpreamble; /* Replication DB preamble. */
long long read_reploff; /* Read replication offset if this is a master. */
long long reploff; /* Applied replication offset if this is a master. */
long long repl_ack_off; /* Replication ack offset, if this is a slave. */
long long repl_ack_time;/* Replication ack time, if this is a slave. */
long long psync_initial_offset; /* FULLRESYNC reply offset other slaves copying this slave output buffer should use. */
char replid[CONFIG_RUN_ID_SIZE+1]; /* Master replication ID (if master). */
int slave_listening_port; /* As configured with: SLAVECONF listening-port */
char slave_ip[NET_IP_STR_LEN]; /* Optionally given by REPLCONF ip-address */
int slave_capa; /* Slave capabilities: SLAVE_CAPA_* bitwise OR. */
multiState mstate; /* MULTI/EXEC state */
int btype; /* Type of blocking op if CLIENT_BLOCKED. */
blockingState bpop; /* blocking state */
long long woff; /* Last write global replication offset. */
list *watched_keys; /* Keys WATCHED for MULTI/EXEC CAS */
dict *pubsub_channels; /* channels a client is interested in (SUBSCRIBE) */
list *pubsub_patterns; /* patterns a client is interested in (SUBSCRIBE) */
sds peerid; /* Cached peer ID. */
listNode *client_list_node; /* list node in client list */
/* Response buffer */
// 記錄buf數組目前使用的字節數
int bufpos;
// (16*1024)=16k,服務器返回給客戶端的內容緩衝區。固定大小,存儲一下固定返回值(如‘ok’)
char buf[PROTO_REPLY_CHUNK_BYTES];
} client;
複製代碼
服務器記錄了redis服務器全部的信息,包括前面介紹的一些,羅列主要的以下:
struct redisServer {
...
// 全部數據信息
redisDb *db;
// 全部客戶端信息
list *clients;
/* time cache */
// 系統當前unix時間戳,秒
time_t unixtime; /* Unix time sampled every cron cycle. */
time_t timezone; /* Cached timezone. As set by tzset(). */
int daylight_active; /* Currently in daylight saving time. */
// 系統當前unix時間戳,毫秒
long long mstime; /* Like 'unixtime' but with milliseconds resolution. */
// 默認沒10s更新一次的時鐘緩存,用於計算鍵idle時長
unsigned int lruclock; /* Clock for LRU eviction */
// 抽樣相關的參數
struct {
// 上次抽樣時間
long long last_sample_time; /* Timestamp of last sample in ms */
// 上次抽樣時,服務器已經執行的命令數
long long last_sample_count;/* Count in last sample */
// 抽樣結果
long long samples[STATS_METRIC_SAMPLES];
int idx;
} inst_metric[STATS_METRIC_COUNT];
// 內存峯值
size_t stat_peak_memory; /* Max used memory record */
// 關閉服務器的標識
int shutdown_asap; /* SHUTDOWN needed ASAP */
// bgsave命令子進程的id
pid_t rdb_child_pid; /* PID of RDB saving child */
// bgrewriteaof子進程id
pid_t aof_child_pid; /* PID if rewriting process */
// serverCron執行次數
int cronloops; /* Number of times the cron function run */
...
}
複製代碼