結合PHP 看Redis 字符串 淺析

一 實現原理

1.1 c語言字符串

以空字符串結尾的字符數組,比如hello在C語言中,經過一系列算法分配內存後,再產生出圖中結構代表字符串'hello!~'。

使用長度爲N+1的字符數組來表示字符串,最後總加一個'\0'代表結尾。

    

1.2 php字符串

底層爲C,結構如代碼所示:

  

1.3 redis字符串

和php的字符串有類似之處,redis作者封裝了一個名爲SDS的結構體來表示字符串,如圖所示:

 

 那它爲啥要這樣做呢,我們在下面小結探討~,先說一下結構中各個屬性的作用:

  1. len:

    標識該字符串長度( 結合PHP的字符串結構體想想有什麼好處~ )

  2. free:

    標識該redis字符串未使用空間大小,它有着自己的分配策略(下面簡單介紹)

  3. buf:

    類似一個C語言的字符串,以'\0'結尾,方便重用C語言的一些字符串函數(說白了就是當做C語言的字符串用,但是\0後面會有長度free的空間,對於C函數來說不影響)

  

二 對比

2.1 查詢複雜度 

2.1.1 c語言

由於C語言的字符串結構使然,它只能通過遍歷加計數來獲取字符串長度,很簡單,O(N)

2.1.2 PHP、Redis

這兩個一起說,是因爲它們都在自己的字符串結構體中內置了長度的屬性,直接O(1)獲取該屬性的值即可得到字符串長度

2.2 摳門C之申請與釋放

C作爲可操作內存的一門語言,它是比較摳門的,不會動態爲我們分配內存。我們需要手動去申請內存儲存數據,在我們不需要用到某些內存的時候,也要去手動將其釋放(垃圾回收、GC)。

2.2.1 緩衝區溢出 忘記申請

redis爲什麼要這麼設計呢,我們先簡單瞭解一下C語言中的 緩衝區溢出 問題

 

我們想在hello!~後追加 得到 hello!~ world~! 但是str2意外被修改 

2.2.2 內存泄漏 忘記釋放

標題我們說忘記釋放,和申請分配相對應,如果我們在C中想要做到縮短字符串的操作,比如我們把str1改爲hello之後,沒有去釋放~!所佔用的空間,就會造成 內存泄漏 ,產生空佔內存。

2.2.3 Redis、PHP如何做

PHP作爲一門“高級語言”,有自己的動態分配和GC,Redis也一樣。上面我們瞭解到,C語言的字符串操作相對比較麻煩,那麼它的優點呢?比較明顯的是節省內存,我們也從圖片中得知,PHP和Redis的字符串結構相對於C來說有其他附加的屬性,它們都會佔用一定的空間。反之,我佔用這麼多空間,圖啥?一句話:空間換時間。

Redis作爲一種高性能的緩存服務,它需要對客戶端的請求做到快速響應。字符串的更改操作對於Redis來說也比較頻繁,如果像C那樣每次都需要申請內存和GC,對性能來說影響是比較大的,更不用說像strlen之類的操作了,直接將O(N)變成了O(1),典型的實現空間換時間。

那麼它具體是怎麼做的呢?

2.3 空間換時間

2.3.1 預分配

當我們對某個string類型的key執行set後,如果字符串的長度增長,Redis不僅對字符串分配相應長度的空間,還會進行預分配給一定的未使用空間,該未使用空間的長度由free屬性標識。機制如下:

  1. 如果修改之後len長度小於1MB,就分配和len長度一樣的空間。

  2. 如果修改之後len長度大於1MB,就分配1MB空間。

舉例:如果str修改之後字符串大小爲30MB,那麼實際分配長度爲30MB(len)+1MB(free)+1byte(\0),假如我們繼續修改str 長度爲30MB+500KB,那Redis就不需要重新申請內存空間,直接利用free的1MB空間即可。嗯~ 空間換來了時間

2.3.2 懶釋放

當我們對某個string類型的key執行set後,如果字符串的長度減少,Redis將字符串直接放進當前key對應的空間中,多出來的空間不會被立即釋放,多出來的空間由free屬性標識,以便即將到來的字符串增長操作。舉個例子:

set str helloworld;

set str hello;

set str helloredis;

執行第二、三個命令發生了什麼呢

helloworld\0 佔用11個字符空間,此時free爲0

hello\0 佔用6個字符空間,此時free爲5

hellophp\0 佔用9個空間,php直接利用free的空間,不需要重新申請內存,此時free爲2

2.4 二進制安全

講這點肯定是和C有區別滴,C語言的字符串結尾用\0 空字符來判定。假設有個文本 redis\0!,在C中!是會被忽略的。所以C中的字符串並不能保存比較特殊的數據。得益於Redis的字符串結構體,我們用len屬性來判斷字符串是否結束,所以數據是存的什麼樣,取出來還是原來的樣子。

三 總結

C字符串 Redis字符串
計算長度需要遍歷 直接取len的值
手動申請釋放內存 len和free配合Redis機制不需要考慮C中此類問題
二進制不安全 二進制安全
頻繁操作慢,每次內存分配 頻繁操作快,不需要每次分配內存

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章