帶讀 |《Redis 設計與實現》(英文名:The Design and Implementation of Redis)

LiberHome發表於2023-01-19

Why Redis?

  • Redis是一種執行速度很快,併發很強,跑在記憶體上的NoSql資料庫,支援鍵到五種資料型別的對映。根據Redis官方提供的benchmark測試資料,redis讀的速度是110000次/s,寫的速度是81000次/s,並且實驗證明,資料沒在快取的時候,相同條件下用Jmeter進行壓測,redis的請求處理速度比MySQL高了7倍。另外,在時間區域性性原理很強的場景下,Redis可以從快取迅速響應,而不是去資料庫查sql,尤其是已經放入快取的資料,這表現出了Redis的高效能
  • Redis是單執行緒的,避免了多執行緒上下文的切換和鎖競爭的時間開銷。(在Redis6.0之後在網路讀寫時已替換成多執行緒,但執行命令仍然是單執行緒)
  • Redis指令是原子性的,高併發時不會產生資料異常,Redis 使用 I/O 多路複用技術,可以處理高併發的連線(非阻塞I/O),這意味著能夠讓一個計算單元來處理來自多個客戶端的流請求實現高併發

想要深入瞭解Redis,這本《Redis 設計與實現》就無法繞開了。


第一部分 資料結構與物件

簡單動態字串 SDS

SDS對標的是C語言的String,透過在底層實現陣列buf的基礎上增加len和free欄位,實現了

  1. 常數複雜度獲取字串長度
  2. 杜絕緩衝區溢位
  3. 減少修改字串時帶來的記憶體重分配次數

SDS長這樣

實現的原理也巨簡單,,,C的String不記錄len,每次都只能遍歷String時間複雜度O(n),SDS空間換時間用一個len記錄一下,時間複雜度直接降到O(1),這就能解釋上面的1和2,至於3就是free這個欄位的作用了,redis使用了空間預分配和惰性空間釋放策略,說白了就是SDS修改之後如果變長了,實際分配的空間不僅僅是len,會根據SDS長度定,如果新的SDS小於1M,新的SDS大小就在原基礎上double+1,為啥double,因為這裡會多分配一倍給free,這就是空間預分配;為啥+1byte,因為最後一個位元組保留空字元,為啥保留空字元,為了更好的相容C語言對字串的操作,對的,沒有看錯,Redis底層就是用的標準C語言實現的。如果新的SDS大於1M,free就等於1M,新的SDS就等於舊的SDS+1M+1byte。


連結串列

因為C語言沒有將鏈作為內建資料結構,Redis用ListNode和指標實現了雙向連結串列

typedef struct listNode{
    struct listNode *prev;
    struct listNode *next;
    void *value;
}listNode;

typedef struct list
{
    listNode *head;
    listNode *tail;
    unsigned long len;
    void *(*dup) (void *ptr); //節點值複製函式
    void *(*free) (void *ptr); //節點值釋放函式
    void *(*match) (void *ptr, void *key); //節點值對比函式
};

字典

因為C語言沒有將字典作為內建資料結構,Redis實現如下:

typedef struct dictht {

    // 雜湊表陣列
    dictEntry **table;

    // 雜湊表大小
    unsigned long size;

    // 雜湊表大小掩碼,用於計算索引值
    // 總是等於 size - 1
    unsigned long sizemask;

    // 該雜湊表已有節點的數量
    unsigned long used;

} dictht;

未完待續。。


參考: 黃健宏. Redis 設計與實現[M]. Ji xie gong ye chu ban she, 2014.
https://zhuanlan.zhihu.com/p/...
https://youle.zhipin.com/ques...

相關文章