天天看點

Redis實作源碼-字元串基礎資料類型分析

        Redis并沒有直接使用C語言中的字元串類型,而是自己設計了一種資料結構,在Redis中,C語言中的字元串隻使用在一些字元串值不會發生修改的地方,比如列印日志。它為什麼要這樣做?這樣做的好處是什麼呢?讓我們先來了解一下這種被稱為Simple dynamic string(SDS)簡單動态字元的結構。

Redis實作源碼-字元串基礎資料類型分析

        SDS遵循了C字元串以空字元結尾的管理,儲存空字元的1位元組空間不計算在SDS的len屬性裡面,并且為空字元配置設定額外的1位元組空間,已經添加空字元到末尾等操作都是自動完成的。為什麼要這麼做?這樣做的好處是SDS可以直接重用一部分C字元串函數庫裡面的函數。

leg: print("%s",s->buf); 
           

        回到剛才的問題,探究SDS存在的意義以及給我們帶來的便捷:

        1.常熟複雜度擷取字元串長度  C字元串并不記錄自身的長度資訊,是以為了擷取一個C字元串的長度,程式必須周遊整個字元串進行累加,複雜度為O(N),而SDS在len屬性中記錄了SDS本身的長度,擷取長度複雜度僅為O(1)。

        2.杜絕緩沖區溢出  除了擷取字元串的複雜度高之外,C字元串不記錄自身長度帶來的另一個問題是容易造成緩沖區溢出(buffer overflow)。當字元串修改時,SDS會先檢查空間是否滿足所需的要求,如果不滿足的話,會将SDS的空間擴充至執行修改所需的大小,是以SDS不需要手動修改SDS的空間大小。

         3.減少修改字元串帶來的記憶體重配置設定次數  C字元串每次增減或者縮短一個字元,程式都要對儲存這個C字元串的數組進行一次記憶體配置設定操作:

        *如果是字元串增長操作,每次增加1個位元組,N次操作就N次記憶體重配置設定,Nginx作為一個高性能的資料庫,資料被修改較頻繁,在這樣的情況下,僅執行記憶體重配置設定就會占用一小部分的時間,如果修改頻繁的話,可能會對性能造成影響。 SDS通過空間預配置設定和惰性空間釋放來減少記憶體重配置設定所需的總時間。 若字元串修改後大小小于1M,則系統多配置設定一倍的空間。leg:修改後25個位元組  則buf數組長度為 25+25+1("/0")=51.若大于1M,至多配置設定1M。 通過這種政策,記憶體重配置設定的次數由必定N次降為最多N次。

        *字元串長度縮小時,空出的空間并不會立刻回收。

二進制安全

       經常說Redis是二進制安全的,這是什麼意思?是指其他類型的資料存儲進去,取出來内容就變了麼?這個了解也對,也不對。首先C語言中的字元串隻能存儲文本檔案,對一下圖檔、檔案不能存儲,這是為什麼呢?“/0”空字元是C語言用來表示字元串的結尾,但同樣以為着你的字元串中不能存在空字元,否則你的資料資訊就會提前結束,也就是說,你存進去的跟你取出來的資料不一樣。Redis中的SDS對每個字元都是平等的,不會對任何字元進行替換、過濾等特殊處理。那麼問題來了,既然SDS中的buf數組跟C的字元串不一樣,那麼它依靠什麼判斷字元的結束? 還記得麼?Simple dynamic string的資料結構中有一個len屬性,存放着buf數組中實際的資料長度,這便是SDS的判斷依靠。

       不看不知道,一看吓一跳。平常在使用Redis的String類型的時候總覺得會是一個簡單的String類型,誰知道底層做了這麼多小而美的細節處理,這種設計思想才是我們最應該學習的。

繼續閱讀