【Redis面試題】Redis的字符串是怎麼實現的?

年前本人在找工做面試時在Redis相關問題上可栽了跟頭。在面試前按常規套路準備了一下,好比 Redis 的經常使用5種數據結構,Redis持久化策略,Redis實現分佈式鎖,簡單發佈訂閱等等都準備了,當時不知天高地厚覺得十拿九穩了,但是萬萬沒想到我終究仍是在Redis的被問的第一個問題上翻船了~~面試

面試官 :看你簡歷上寫了熟悉經常使用數據結構,都有哪些說說
本人 :經常使用有5種,string,list,set,zset,hash(心裏很得意)算法

面試官 :那你說說都用過哪些數據結構
本人 :用的最多的是string,一般會把json字符串存進去json

面試官 :那你知道Redis內部是怎麼實現它的string的麼?
本人 :呃~,我瞭解Redis是用C語言寫的,至於具體實現就不清楚了~api

到此一面卒~~~
有相同經歷的朋友麼?數組


回去後惡補了一下Redis有關原理性的知識點,剛好最近在最總結面試經歷因而有了今天這篇文章。bash

本篇會講一下內容:數據結構

  • Redis字符串的實現
  • Redis字符串的性能優點

Redis字符串的實現

Redis雖然是用C語言寫的,但卻沒有直接用C語言的字符串,而是本身實現了一套字符串。目的就是爲了提高速度,提高性能,能夠看出Redis爲了高性能也是煞費苦心。分佈式

Redis構建了一個叫作簡單動態字符串(Simple Dynamic String),簡稱SDS函數

1.SDS 代碼結構源碼分析

struct sdshdr{
    //  記錄已使用長度
    int len;
    // 記錄空閒未使用的長度
    int free;
    // 字符數組
    char[] buf;
};
複製代碼

SDS ?什麼鬼?可能對此陌生的朋友對這個名稱有疑惑。只是個名詞而已沒必要在乎,咱們要重點欣賞借鑑Redis的設計思路。 下面畫個圖來講明,一目瞭然。

Redis的字符串也會遵照C語言的字符串的實現規則,即最後一個字符爲空字符。然而這個空字符不會被計算在len裏頭。

2.SDS 動態擴展特色

SDS的最厲害最奇妙之處在於它的Dynamic。動態變化長度。舉個例子

如上圖所示剛開始s1 只有5個空閒位子,後面須要追加' world' 6個字符,很明顯是不夠的。那咋辦?Redis會作一下三個操做:

  • 1.計算出大小是否足夠
  • 2.開闢空間至知足所需大小
  • 3.開闢與已使用大小len相同長度的空閒free空間(若是len < 1M)開闢1M長度的空閒free空間(若是len >= 1M

看到這兒爲止有沒有朋友以爲這個實現跟Java的列表List實現有點相似呢?看完後面的會以爲更像了。

Redis字符串的性能優點

  • 快速獲取字符串長度
  • 避免緩衝區溢出
  • 下降空間分配次數提高內存使用效率

1.快速獲取字符串長度

在看下上面的SDS結構體:

struct sdshdr{
    //  記錄已使用長度
    int len;
    // 記錄空閒未使用的長度
    int free;
    // 字符數組
    char[] buf;
};
複製代碼

因爲在SDS裏存了已使用字符長度len,因此當想獲取字符串長度時直接返回len便可,時間複雜度爲O(1)。若是使用C語言的字符串的話它的字符串長度獲取函數時間複雜度爲O(n),n爲字符個數,由於他是從頭至尾(到空字符'\0')遍歷相加。

2.避免緩衝區溢出

對一個C語言字符串進行strcat追加字符串的時候須要提早開闢須要的空間,若是不開闢空間的話可能會形成緩衝區溢出,而影響程序其餘代碼。以下圖,有一個字符串s1="hello" 和 字符串s2="baby",如今要執行strcat(s1,"world"),而且執行前未給s1開闢空間,因此形成了緩衝區溢出。

而對於Redis而言因爲每次追加字符串時都會檢查空間是否夠用,因此不會存在緩衝區溢出問題。每次追加操做前都會作以下操做:

  • 1.計算出大小是否足夠
  • 2.開闢空間至知足所需大小

3.下降空間分配次數提高內存使用效率

字符串的追加操做會涉及到內存分配問題,然而內存分配問題會牽扯內存劃分算法以及系統調用因此若是頻繁發生的話影響性能,因此對於性能至上的Redis來講這是萬萬不能忍受的。因此採起了一下兩種優化措施

  • 空間與分配
  • 惰性空間回收

1. 空間預分配

對於追加操做來講,Redis不只會開闢空間至夠用並且還會預分配未使用的空間(free)來用於下一次操做。至於未使用的空間(free)的大小則由修改後的字符串長度決定。

當修改後的字符串長度len < 1M,則會分配與len相同長度的未使用的空間(free) 當修改後的字符串長度len >= 1M,則會分配1M長度的未使用的空間(free)

有了這個預分配策略以後會減小內存分配次數,由於分配以前會檢查已有的free空間是否夠,若是夠則不開闢了~

2. 惰性空間回收

與上面狀況相反,惰性空間回收適用於字符串縮減操做。好比有個字符串s1="hello world",對s1進行sdstrim(s1," world")操做,執行完該操做以後Redis不會當即回收減小的部分,而是會分配給下一個須要內存的程序。固然,Redis也提供了回收內存的api,能夠本身手動調用來回收縮減部分的內存。

到此爲止結束了~ 下次在遇到這個問題能夠侃侃而談了,哈哈哈😂


其餘文章

【Spring面試題】Spring 爲啥默認把bean設計成單例的?

Spring 源碼分析之 bean 實例化原理?

Spring 源碼分析之 bean 依賴注入原理(注入屬性)

Spring源碼分析之 lazy-init 實現原理

相關文章
相關標籤/搜索