第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

首頁(yè) 慕課教程 后端通用面試教程 后端通用面試教程 Redis數(shù)據(jù)結(jié)構(gòu)(一)

1. 前言

簡(jiǎn)單來(lái)說(shuō),Redis(Remote Dictionary Server)也是一個(gè)數(shù)據(jù)庫(kù),不過(guò)和傳統(tǒng)數(shù)據(jù)庫(kù)不同點(diǎn)在于 Redis 的數(shù)據(jù)存儲(chǔ)在內(nèi)存中,所以讀寫速度遠(yuǎn)超傳統(tǒng)數(shù)據(jù)庫(kù)(例如 MySQL ),同時(shí)因?yàn)?code>Key—Value的數(shù)據(jù)存儲(chǔ)形式非常零活,所以Redis被廣泛應(yīng)用在緩存方向,并且能適配各種實(shí)戰(zhàn)的應(yīng)用場(chǎng)景。

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

掌握 MySQL 是后端開發(fā)的必備技能,掌握 Redis 則是一個(gè)簡(jiǎn)歷加分項(xiàng)。

我們?cè)谥暗恼鹿?jié)對(duì) MySQL 常見(jiàn)的題目進(jìn)行了剖析, 在本章中我們會(huì)介紹 Redis 相關(guān)的高頻面試題。

2. Redis底層數(shù)據(jù)結(jié)構(gòu)

面試官提問(wèn): 你有看過(guò) Redis 源碼嗎?Redis 底層是用什么數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的?

題目解析:

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

Redis 底層是用標(biāo)準(zhǔn) C 語(yǔ)言編寫的,下面我們會(huì)結(jié)合 C 代碼分析。

2.1 SDS 數(shù)據(jù)結(jié)構(gòu)

2.1.1 定義

字符串是 Redis 中最常用的數(shù)據(jù)類型。

Redis 里的五種基礎(chǔ)數(shù)據(jù)類型都是以唯一字符串作為 Key,通過(guò)這個(gè) Key 映射不同的 Value 數(shù)據(jù),不同數(shù)據(jù)類型的差異在于存儲(chǔ) Value 不同,String 類型的 Value 是字符串,List 類型的 Value 是列表。

Redis 沒(méi)有使用 C 語(yǔ)言原生的字符數(shù)組表示字符串,而是定義了一個(gè)字符串結(jié)構(gòu)體 SDS(Simple Dynamic String,簡(jiǎn)單動(dòng)態(tài)字符串)。

SDS的結(jié)構(gòu)體定義如下:

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

然后候選人可以結(jié)合畫圖的方式解釋SDS的數(shù)據(jù)結(jié)構(gòu):

圖片描述
?

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

如上圖所示,當(dāng)前字符串長(zhǎng)度len = 4,未使用長(zhǎng)度f(wàn)ree = 0,buf數(shù)組存儲(chǔ)的內(nèi)容是"mooc\0"。

注意:C 語(yǔ)言中的字符串都是以'\0'結(jié)尾,并且計(jì)算len值時(shí)不包含末尾的'\0';另外,長(zhǎng)度為零的數(shù)組即char[] buf不占用內(nèi)存。

其次,我們需要闡述 SDS 數(shù)據(jù)結(jié)構(gòu)的優(yōu)點(diǎn):

2.1.2 常量時(shí)間獲取字符串長(zhǎng)度

對(duì)于傳統(tǒng)的 C 字符串,如果要獲取字符串長(zhǎng)度,例如調(diào)用strlen()函數(shù),會(huì)從頭開始遍歷字符串直到遇到'\0',時(shí)間復(fù)雜度為O(n);

對(duì)于 SDS 數(shù)據(jù)結(jié)構(gòu),能直接訪問(wèn)len屬性獲取字符串長(zhǎng)度,時(shí)間復(fù)雜度為O(1) ,獲取字符串長(zhǎng)度的操作不會(huì)成為 Redis 的性能瓶頸。

2.1.3 避免緩沖區(qū)溢出

傳統(tǒng) C 語(yǔ)言字符串不會(huì)記錄本身長(zhǎng)度,在復(fù)制字符串的時(shí)候,如果分配的內(nèi)存不夠,會(huì)造成緩沖區(qū)溢出。

SDS 復(fù)制字符串時(shí),會(huì)首先檢查 free 變量,判斷內(nèi)存空間是否符合復(fù)制要求,如果不滿足的話,會(huì)將空間擴(kuò)展到所需要的程度,再執(zhí)行復(fù)制操作,不存在緩沖區(qū)溢出的問(wèn)題。

2.1.4 減少內(nèi)存分配次數(shù)

傳統(tǒng) C 語(yǔ)言在對(duì)字符串進(jìn)行修改時(shí),會(huì)對(duì)數(shù)組重新分配內(nèi)存,這個(gè)過(guò)程可能會(huì)涉及操作系統(tǒng)的系統(tǒng)調(diào)用,耗時(shí)較長(zhǎng)。

在 Redis 中, SDS 提供了空間預(yù)分配和惰性空間釋放兩種優(yōu)化規(guī)則:

規(guī)則一:空間預(yù)分配規(guī)則

調(diào)用 SDS 的 API 對(duì) SDS 進(jìn)行修改時(shí),API 不僅會(huì)分配本次修改需要的內(nèi)存空間,還會(huì)分配額外的內(nèi)存空間。

這里遵循的兩種定量分配規(guī)則:

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

  • sdshdr的len字段值 > 1MB時(shí),會(huì)分配1MB的未使用空間。

其中檢測(cè)是否超過(guò)容量的C語(yǔ)言方法:

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

圖片描述

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

如上圖所示,我們?cè)?"mooc" 的基礎(chǔ)上添加了’s’的字符,并且預(yù)留了一個(gè)字符的空間,所以free=1,

規(guī)則二:惰性空間釋放規(guī)則

當(dāng)調(diào)用 SDS API縮短 SDS 字符串時(shí),程序不會(huì)立即回收內(nèi)存空閑的字節(jié),而是使用上述結(jié)構(gòu)體中的free字段將空閑字節(jié)記錄下來(lái),將來(lái)如果有修改操作,則直接使用已分配的空閑內(nèi)存,避免了重新分配。

2.1.5 二進(jìn)制安全

SDS可以存儲(chǔ)任何二進(jìn)制數(shù)據(jù),包括'\0',因?yàn)榕袛?SDS 是否達(dá)到字符串終點(diǎn)不是通過(guò)末尾'\0'判斷,而是通過(guò)len字段值。

傳統(tǒng) C 語(yǔ)言則是遇到'0'則判定字符串結(jié)束,所以傳統(tǒng)字符串不能存儲(chǔ)圖片、視頻等文件(遇到編碼'\0'則數(shù)據(jù)會(huì)被截?cái)啵?/p>

3. 小結(jié)

本章節(jié)介紹了 Redis 最基礎(chǔ)的 SDS 數(shù)據(jù)結(jié)構(gòu),有兩點(diǎn)需要重點(diǎn)關(guān)注:

(1)Redis底層對(duì) C 語(yǔ)言自帶數(shù)據(jù)結(jié)構(gòu)進(jìn)行了封裝優(yōu)化。

(2)Redis 作為內(nèi)存數(shù)據(jù)庫(kù),不能避免頻繁的修改和查詢操作,所以在設(shè)計(jì)最初,各種數(shù)據(jù)結(jié)構(gòu)和操作實(shí)現(xiàn)的核心目的就是為了追求速度,遍歷和內(nèi)存分配等耗時(shí)的操作是難以忍受的。