相信用過Redis的人都知道,Redis提供了一個邏輯上的對象系統構建了一個鍵值對數據庫以供客戶端用戶使用。這個對象系統包括字符串對象,哈希對象,列表對象,集合對象,有序集合對象等。可是Redis面向內存並無直接使用這些對象。而是使用了簡單動態字符串,鏈表,字典(散列表),跳躍表,整數集合,壓縮列表這些數據結構來操做內存。html
Redis默認並未直接使用C字符串(C字符串僅僅做爲字符串字面量,用在一些無需對字符串進行修改的地方,如打印日誌)。而是以Struct的形式構造了一個SDS的抽象類型。當Redis須要一個能夠被修改的字符串時,就會使用SDS來表示。在Redis數據庫裏,包含字符串值的鍵值對都是由SDS實現的(Redis中全部的鍵都是由字符串對象實現的即底層是由SDS實現,Redis中全部的值對象中包含的字符串對象底層也是由SDS實現)。程序員
struct sdshdr{ //int 記錄buf數組中未使用字節的數量 如上圖free爲0表明未使用字節的數量爲0 int free; //int 記錄buf數組中已使用字節的數量即sds的長度 如上圖len爲5表明未使用字節的數量爲5 int len; //字節數組用於保存字符串 sds遵循了c字符串以空字符結尾的慣例目的是爲了重用c字符串函數庫裏的函數 char buf[]; }
上圖表示了SDS與C字符串的區別,關於爲何Redis要使用SDS而不是C字符串,咱們能夠從如下幾個方面來分析。redis
C字符串,若是程序員在字符串修改的時候若是忘記給字符串從新分配足夠的空間,那麼就會發生內存溢出,如上圖所示,忘記給s1分配足夠的內存空間, s1的數據就會溢出到s2的空間, 致使s2的內容被修改.。而Redis提供的SDS其內置的空間分配策略則能夠徹底杜絕這種事情的發生。當API須要對SDS進行修改時, API會首先會檢查SDS的空間是否知足條件, 若是不知足, API會自動對它動態擴展, 而後再進行修改。數據庫
在C字符串中,若是對字符串進行修改,那麼咱們就不得不面臨內存重分配。由於C字符串是由一個N+1長度的數組組成,若是字符串的長度變長,咱們就必須對數組進行擴容,不然會產生內存溢出。而若是字符串長度變短,咱們就必須釋放掉再也不使用的空間,不然會發生內存泄漏。數組
對於Redis這種具備高性能要求的內存數據庫,若是每次修改字符串都要進行內存重分配,無疑是巨大的性能損失。而Redis的SDS提供了兩種空間分配策略來解決這個問題。安全
空間預分配數據結構
咱們知道在數組進行擴容的時候,每每會申請一個更大的數組,而後把數組複製過去。爲了提高性能,咱們在分配空間的時候並非分配一個剛恰好的空間,而是分配一個更大的空間。Redis一樣基於這種策略提供了空間預分配。當執行字符串增加操做而且須要擴展內存時,程序不只僅會給SDS分配必需的空間還會分配額外的未使用空間,其長度存到free屬性中。其分配策略以下:併發
惰性空間釋放運維
惰性空間釋放用於字符串縮短的操做。當字符串縮短是,程序並非當即使用內存重分配來回收縮短出來的字節,而是使用free屬性記錄起來,並等待未來使用。函數
Redis經過空間預分配和惰性空間釋放策略在字符串操做中必定程度上減小了內存重分配的次數。但這種策略一樣會形成必定的內存浪費,所以Redis SDS API提供相應的API讓咱們在有須要的時候真正的釋放SDS的未使用空間。
C字符串中的字符必須符合某種編碼(好比ASCII),而且除了字符串的末尾以外,字符串裏面不能包含空字符,不然最早被程序讀入的空字符將被誤認爲是字符串結尾,這些限制使得C字符串只能保存文本數據,而不能保存像圖片、音頻、視頻、壓縮文件這樣的二進制數據。若是有一種使用空字符來分割多個單詞的特殊數據格式,就不能用C字符串來表示,如"Redis\0String",C字符串的函數會把'\0'當作結束符來處理,而忽略到後面的"String"。而SDS的buf字節數組不是在保存字符,而是一系列二進制數組,SDS API都會以二進制的方式來處理buf數組裏的數據,使用len屬性的值而不是空字符來判斷字符串是否結束。
咱們來看幾個Redis常見操做的時間複雜度。
Redis在獲取字符串長度上的時間複雜度爲常數級O(1)。
經過以上分析,咱們能夠獲得,SDS這種數據結構相對於C字符串有如下優勢:
Redis定位於一個高性能的內存數據庫,其面向的就是大數據量,大併發,頻繁讀寫,高響應速度的業務。所以在保證安全穩定的狀況下,性能的提高很是重要。而SDS這種數據結構屏蔽了C字符串的一些缺點,能夠提供安全高性能的字符串操做。
Redis在互聯網項目中的應用愈來愈普遍,會用只是學習Redis中最簡單的一步,要想真正的成爲Redis高手,瞭解其底層的實現必不可少。本篇文章簡單介紹了Redis中SDS數據結構及其特性,分析了Redis SDS的空間分配策略和其與C字符串相比的優點,後續的文章將繼續分享Redis底層實現的其它數據結構。未完待續......
《Redis設計與實現》
《Redis開發與運維》
《Redis官方文檔》