亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

1. 前言

簡單來說,Redis(Remote Dictionary Server)也是一個數據庫,不過和傳統數據庫不同點在于 Redis 的數據存儲在內存中,所以讀寫速度遠超傳統數據庫(例如 MySQL ),同時因為Key—Value的數據存儲形式非常零活,所以Redis被廣泛應用在緩存方向,并且能適配各種實戰的應用場景。

Redis 提供了多種語言的 API ,比如常見的 Java、C++、Python 等,基本是后端開發中最常用的緩存中間件。

掌握 MySQL 是后端開發的必備技能,掌握 Redis 則是一個簡歷加分項。

我們在之前的章節對 MySQL 常見的題目進行了剖析, 在本章中我們會介紹 Redis 相關的高頻面試題。

2. Redis底層數據結構

面試官提問: 你有看過 Redis 源碼嗎?Redis 底層是用什么數據結構實現的?

題目解析:

這里談到的數據結構不是 Redis 的五種對外基本數據結構:String(字符串類型)、Hash(哈希類型)、List(鏈表類型)、Set(集合類型)、ZSet(有序集合類型),而是更為底層的數據結構實現,例如雙向鏈表、字典、壓縮列表等。

Redis 底層是用標準 C 語言編寫的,下面我們會結合 C 代碼分析。

2.1 SDS 數據結構

2.1.1 定義

字符串是 Redis 中最常用的數據類型。

Redis 里的五種基礎數據類型都是以唯一字符串作為 Key,通過這個 Key 映射不同的 Value 數據,不同數據類型的差異在于存儲 Value 不同,String 類型的 Value 是字符串,List 類型的 Value 是列表。

Redis 沒有使用 C 語言原生的字符數組表示字符串,而是定義了一個字符串結構體 SDS(Simple Dynamic String,簡單動態字符串)。

SDS的結構體定義如下:

struct sdshdr{
     //記錄buf數組中已使用字節的數量,等于SDS中已使用字符串的長度
     int len;
     //記錄buf數組中未使用字節的數量
     int free;
     //字節數組,用于保存字符串
     char buf[];
}

然后候選人可以結合畫圖的方式解釋SDS的數據結構:

圖片描述
?

("mooc"字符串存儲示意圖)

如上圖所示,當前字符串長度len = 4,未使用長度free = 0,buf數組存儲的內容是"mooc\0"

注意:C 語言中的字符串都是以'\0'結尾,并且計算len值時不包含末尾的'\0';另外,長度為零的數組即char[] buf不占用內存。

其次,我們需要闡述 SDS 數據結構的優點:

2.1.2 常量時間獲取字符串長度

對于傳統的 C 字符串,如果要獲取字符串長度,例如調用strlen()函數,會從頭開始遍歷字符串直到遇到'\0',時間復雜度為O(n);

對于 SDS 數據結構,能直接訪問len屬性獲取字符串長度,時間復雜度為O(1) ,獲取字符串長度的操作不會成為 Redis 的性能瓶頸。

2.1.3 避免緩沖區溢出

傳統 C 語言字符串不會記錄本身長度,在復制字符串的時候,如果分配的內存不夠,會造成緩沖區溢出。

SDS 復制字符串時,會首先檢查 free 變量,判斷內存空間是否符合復制要求,如果不滿足的話,會將空間擴展到所需要的程度,再執行復制操作,不存在緩沖區溢出的問題。

2.1.4 減少內存分配次數

傳統 C 語言在對字符串進行修改時,會對數組重新分配內存,這個過程可能會涉及操作系統的系統調用,耗時較長。

在 Redis 中, SDS 提供了空間預分配和惰性空間釋放兩種優化規則:

規則一:空間預分配規則

調用 SDS 的 API 對 SDS 進行修改時,API 不僅會分配本次修改需要的內存空間,還會分配額外的內存空間。

這里遵循的兩種定量分配規則:

  • sdshdr的len字段值 < 1MB時,會分配和len同樣大小的未使用空間;

  • sdshdr的len字段值 > 1MB時,會分配1MB的未使用空間。

其中檢測是否超過容量的C語言方法:

static int checkStringLength(client *c, long long size) {
    // 字符串長度為512M,如果超過,直接拋出錯誤,結束函數
    if (size > 512*1024*1024) {
        addReplyError(c,"string exceeds maximum allowed size (512MB)");
        return C_ERR;
    }
    return C_OK;
}

圖片描述

(SDS重新分配空間示意圖)

如上圖所示,我們在 "mooc" 的基礎上添加了’s’的字符,并且預留了一個字符的空間,所以free=1,

規則二:惰性空間釋放規則

當調用 SDS API縮短 SDS 字符串時,程序不會立即回收內存空閑的字節,而是使用上述結構體中的free字段將空閑字節記錄下來,將來如果有修改操作,則直接使用已分配的空閑內存,避免了重新分配。

2.1.5 二進制安全

SDS可以存儲任何二進制數據,包括'\0',因為判斷 SDS 是否達到字符串終點不是通過末尾'\0'判斷,而是通過len字段值。

傳統 C 語言則是遇到'0'則判定字符串結束,所以傳統字符串不能存儲圖片、視頻等文件(遇到編碼'\0'則數據會被截斷)

3. 小結

本章節介紹了 Redis 最基礎的 SDS 數據結構,有兩點需要重點關注:

(1)Redis底層對 C 語言自帶數據結構進行了封裝優化。

(2)Redis 作為內存數據庫,不能避免頻繁的修改和查詢操作,所以在設計最初,各種數據結構和操作實現的核心目的就是為了追求速度,遍歷和內存分配等耗時的操作是難以忍受的。