Redis資料結構(一)簡單動態字串,redis資料結構
Redis的字串採用的是自訂的struct,名字叫做簡單動態字串(simple dynamic string,SDS)。
結構如下:
struct sdshdr{int len;int free;char buf[];};
採用如此結構的好處是:
【1】擷取length的時候覆雜度為O(1),不需要O(n);
【2】動態分配空間,避免緩衝區溢位,避免每次修改或者append都重新分配;
【3】二進位安全;
關於第一點顯而易見,第二點,為了減少修改字串帶來的記憶體重分配次數,redis採用了2個措施:
1)空間預分配;假設如下sds(狀態【0】),執行命令【sdscat(s,”redis”)】往其中append一個字串“redis”之後將變為狀態【1】,發生了什麼呢?當sds發現當前free長度無法分配新添加的字串時,將發生一次空間分配,如果修改之後sds長度小於1MB,則會分配總長度為【修改後長度】*2+1,即為(5+5)*2+1,1為結尾符號空間。如果修改後sds總長度大於等於1MB,則會分配【修改後總長度】+1MB的長度。假設再次執行命令【sdscat(s,”redis”)】,由於目前狀態【1】free=10,有足夠空間分配新加入的字串,則不會發生空間分配操作,變為狀態【2】;
狀態【0】
struct sdshdr{ int len=5; int free=4; char buf[]={'h','e','l','l','o','\0','','','',''}; };
狀態【1】:
struct sdshdr{ int len=10; int free=10; char buf[]={'h','e','l','l','o','r','e','d','i','s','\0',...}; };
狀態【2】:
struct sdshdr{ int len=15; int free=5; char buf[]={'h','e','l','l','o','r','e','d','i','s','r','e','d','i','s','\0',...}; };
2)惰性空間釋放;現在假設從狀態【2】執行命令【sdstrim(s,”re”)】,移除sds中所有的’r’,’e’,將轉換為狀態【3】; 此時並沒有釋放空間,len+free依然等於20;這樣做的目的是避免了縮短字串時的記憶體重分配操作,並且未將來的字串擴充預留了空間 ; 當然也可以使用【sdsfree】真正釋放sds;
狀態【3】:
struct sdshdr{ int len=11; int free=9; char buf[]={'h','e','l','l','o','d','i','s','d','i','s','\0',...}; };
關於第三點,SDS的buf屬性稱為位元組數組,儲存的是位元據;同時由於使用len欄位來判斷字串是否結束,所以是安全的,不會出現c字串中以空格作為結尾判斷,導致字串被截斷的問題。