
redis 的源碼都是用C語言寫的,而且,整個redis的大小都很小。本文主要參考redis設(shè)計與實現(xiàn)一書,書中部分數(shù)據(jù)與最新的redis源碼有所出入,但不影響我們的使用學(xué)習(xí)。
基本類型:
建議這里的學(xué)習(xí)大家去參看一下源代碼,或者相應(yīng)的書籍,本節(jié)不對具體的細節(jié)做討論,僅總結(jié)沒種數(shù)據(jù)類型的作用。
1.動態(tài)字符串
struct sdshdr{
// 記錄字符長度,不計 “\0”
int len;
// 剩余空間
int free;
// 字符數(shù)組
char buff[];
}
重定義c語言中的字符串,使得字符串的大小可以改變,減少因為字符串大小改變導(dǎo)致的頻繁釋放內(nèi)存,分配內(nèi)存。以適應(yīng)數(shù)據(jù)庫的使用場景。
特點:
- 惰性空間釋放
- 避免過多的字符串
- 兼容底層的c字符串
2.鏈表
redis 內(nèi)置實現(xiàn)了鏈表的結(jié)構(gòu)。
typedef struct listNode {
struct listNode *prev;
struct listNode *next;
// 定義void指針是因為可以指向任何類型
void *value;
} listNode;
typedef struct list {
listNode *head;
listNode *tail;
//復(fù)制函數(shù)
void *(*dup)(void *ptr);
// 釋放函數(shù)
void (*free)(void *ptr);
// 節(jié)點值對比函數(shù)
int (*match)(void *ptr, void *key);
// 節(jié)點數(shù)量
unsigned long len;
} list;
3.字典
鍵值對,redis使用時的鍵值關(guān)系就是靠它了!
typedef struct dictht {
// hash表數(shù)組
dictEntry **table;
unsigned long size;
// 計算索引值的掩碼,大小為 size - 1
unsigned long sizemask;
// 已使用節(jié)點數(shù)
unsigned long used;
} dictht;
typedef struct dictEntry {
// 鍵值對
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;
} dictEntry;
// 字典中自帶了兩個hash表,一個平常使用,一個rehash的時候使用。rehash不是一次完成的,而是漸進完成的。
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
int iterators; /* number of iterators currently running */
} dict;
還有一些具體的類型定義等,可以參考源代碼。
4.跳躍表
跳躍表是一個有序的結(jié)構(gòu),先安分數(shù)排序,再按對象大小排序,聽起來是不是很像sorted set,我猜就是這種數(shù)據(jù)的底層結(jié)構(gòu)。一個跳躍表的例子:

5.整數(shù)集合
該數(shù)據(jù)結(jié)構(gòu)可以很便利的用于存取數(shù)據(jù)庫中的整數(shù)集合。
typedef struct intset {
uint32_t encoding;
uint32_t length;
int8_t contents[];
} intset;
redis的整數(shù)集合還會有一定的升級和降級的操作以節(jié)省內(nèi)存。
6.壓縮列表
為了節(jié)約內(nèi)存而開發(fā)的,對于可以壓縮的小數(shù)值,小的字符串等進行鍵的壓縮。
7.對象
redis基于以上各種數(shù)據(jù)結(jié)構(gòu),建立相應(yīng)的對象結(jié)構(gòu),從而以對象構(gòu)建數(shù)據(jù)庫。對象包括:字符串對象,列表對象,集合對象,有序集合對象,哈希對象。除此之外,redis還引入引用計數(shù)和內(nèi)存回收機制。
8.數(shù)據(jù)庫
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 */
struct evictionPoolEntry *eviction_pool; /* Eviction pool of keys */
int id; /* Database ID */
long long avg_ttl; /* Average TTL, just for stats */
} redisDb;
- struct redisServer
- typedef struct client
定義了客戶端和服務(wù)器。
Redis 持久化
你可以理解為mysql一樣的在內(nèi)存留下的數(shù)據(jù)。
1.RDB 持久化
一定間隔內(nèi)將內(nèi)存的數(shù)據(jù)保存的本地的RDB文件中,它保存了某個時間點得數(shù)據(jù)集,非常適用于數(shù)據(jù)集的備份。
優(yōu)點:與AOF相比,在恢復(fù)大的數(shù)據(jù)集的時候,RDB方式會更快一些。
缺點:萬一在Redis意外宕機, 你可能會丟失間隔時間內(nèi)的數(shù)據(jù).
2.AOF 持久化
一個只進行追加的日志文件,記錄下每次執(zhí)行的命令,復(fù)原的時候把所有命令執(zhí)行一遍,不改變數(shù)據(jù)以及運行狀態(tài)的命令不記錄。過大的時候整理重寫。
優(yōu)點:備份時間極短。數(shù)據(jù)丟失量小。
缺點:AOF 文件的體積通常要大于 RDB 文件的體積。
關(guān)于持久化的詳細信息可參考官網(wǎng)指南
事務(wù)
MULTI 、 EXEC 、 DISCARD 和 WATCH 是 Redis 事務(wù)相關(guān)的命令。事務(wù)可以一次執(zhí)行多個命令, 并且?guī)в幸韵聝蓚€重要的保證:
- 事務(wù)是一個單獨的隔離操作:事務(wù)中的所有命令都會序列化、按順序地執(zhí)行。事務(wù)在執(zhí)行的過程中,不會被其他客戶端發(fā)送來的命令請求所打斷。
- 事務(wù)是一個原子操作:事務(wù)中的命令要么全部被執(zhí)行,要么全部都不執(zhí)行。
Redis 不支持回滾(roll back):
Redis 命令只會因為錯誤的語法而失敗(并且這些問題不能在入隊時發(fā)現(xiàn)),或是命令用在了錯誤類型的鍵上面:這也就是說,從實用性的角度來說,失敗的命令是由編程錯誤造成的,而這些錯誤應(yīng)該在開發(fā)的過程中被發(fā)現(xiàn),而不應(yīng)該出現(xiàn)在生產(chǎn)環(huán)境中。
因為不需要對回滾進行支持,所以 Redis 的內(nèi)部可以保持簡單且快速。
有種觀點認為 Redis 處理事務(wù)的做法會產(chǎn)生bug, 然而需要注意的是, 在通常情況下, 回滾并不能解決編程錯誤帶來的問題。 舉個例子, 如果你本來想通過 INCR 命令將鍵的值加上 1 , 卻不小心加上了 2 , 又或者對錯誤類型的鍵執(zhí)行了INCR , 回滾是沒有辦法處理這些情況的。
本人對于redis的 實操尚少,更多的知識來自書籍和官網(wǎng),本文尚介紹部分redis的特性,更多的學(xué)習(xí)可以參考官網(wǎng)文檔