redis底層資料結構之sds

來源:互聯網
上載者:User

標籤:字串   redis   sds   


最近,我想通過redis的源碼來學習redis。雖然平時工作中用得不多,不過對redis還是比較感興趣的,畢竟它的效能是不錯的。redis是一個開源的項目,我們可以通過原始碼去瞭解redis。我後面會通過自己的學習,寫一些關於redis源碼的文章。文章的主要內容是分析代碼設計,而並不會對源碼進行詳細解說。如果有不對的地方,請指正。源碼是reids 3.0.3版本。

sds

一、redis的字串 sds:

c語言的字串是 char *,redis則自己定義了內部的字串sds,同時也提供了一組sds的相關的操作函數,redis這樣做是為了更方便地使用字串。先看看sds的定義:

typedef char *sds;struct sdshdr {    unsigned int len;    //字串長度    unsigned int free;    //剩餘空間大小    char buf[];        //字串儲存地址};

先看 struct sdshdr,這是redis字串的頭部資訊,各欄位意思見注釋。這樣的字串設計,是與字串的相關操作關聯的。為更好地瞭解sds,先舉個例子看一下sds是如何的儲存“hello world”的。下面一一個例子:

len = 11free = 5buf[] = “hello world” + ’\0’ + 5個byte空間

其中 len=11 指的是字串的長度,free=5是指sds還剩餘的儲存空間,buf的實際大小為 len + 1 + free,其中 1 是指 ‘\0’ 的空間大小。

從資料結構的定義和上面例子可以看出:

    1. len是字串的長度,通過記錄下長度,對於求字串長度的操作是O(1)的,但這樣也帶來了len的維護問題,在更新字串長度時,同時需要更新len欄位。

    2. free是剩餘空間,剩餘的空間是用於擴充字元串長度。但是剩餘的空間是有限的,為保證字串長度能自由增長,需要一些方法去增加剩餘的空間。

    3. buf字串儲存的地址,把buf聲明為0長度的char數組,是常用的內容空間分配設計方法。一般情況下,這樣的資料結構都是動態申請空間的。這樣就能在動態申請空間時,分配 資料結構本身的大小+buf大小 的空間,做到動申請,現時也能通過重新申請的方法擴充空間。具體可見 sdsnewlen 和 sdsMakeRoomFor 函數的實現。

    4. ’\0’,字串後面總是以‘\0’結尾,目的是讓sds的buf跟c語言的字串對齊,能夠通過printf等函數直接輸出。但這裡需要注意‘\0’並不能代表sds字串的結束,因為sds字串中也可能含有’\0’字元。字串的結束只能由len指定。這樣設計的字串,不僅可以支援c語言的字串,還可以擴充到二進位串,就像std::string一樣。

    5. 支援二進位儲存。由於sds的字串長度是由len指定的,字串中的每一個byte,都可以覆蓋二進位中的所有值,所以可以支援二進位儲存。redis為什麼要支援二進位串的儲存呢?我想是因為redis需要間接支援複雜資料結構的儲存。因為redis本身只提供了string,list,set,zset,hash等通用的資料結構的儲存,對於使用者自己定義的資料結構,redis並不能直接支援其儲存。而redis提供了二進位的儲存,可以讓使用者自己把複雜的資料結構序列化成二進位串再儲存。這樣雖然在存時需要序列化、取時需要還原序列化,消耗了CPU,但卻換來的儲存的通用性。

    6. typedef char * sds。sds定義為char*而不是sdshdr*。主要的原因是為了使sds看起來更像char*,在使用一些函數時能跟char*無區別,如printf。這樣做也是有代價的,那便是sds相關操作的複雜性提高了。幾乎每個有關sds的操作,都要通過sds(char*)來定位出sdshdr*。

sds也有一些缺點:

sdshdr佔用一定空間
字串中有剩餘空間,雖然可用於擴充,但也可能是一種浪費。
需要代碼本身提供一組相關的操作函數,才能更好地使用。
使用sds時,需要比較瞭解sds的特點和其操作函數的使用規範,對編程者要求比較高。


二、sds的相關操作函數:


為了讓使用者更方便地使用sds,redis提供了一系列關於sds的操作函數。如:

sdslen 字串長度sdsavail 字串可用空間長度(free)sdsnew 建立字串sdsdup 複製字串sdsfree 釋放字串sdscat 字串拼接…

這裡不一一列舉。
sds和其操作函數,一同為redis提供了操作方便的字串,但同時也增加了字串操作的複雜性。在使用sds及其操作函數時,必須瞭解sds的行為特點才能正確使用。

舉一個函數來分析,sdsMakeRoomFor:

/* Enlarge the free space at the end of the sds string so that the caller * is sure that after calling this function can overwrite up to addlen * bytes after the end of the string, plus one more byte for nul term. * * Note: this does not change the *length* of the sds string as returned * by sdslen(), but only the free buffer space we have. *//* 增在sds的空間,以夠字串長度增加addlen。 * 如果字串本身的剩餘空間大於addlen,則無需增加sds的空間,否則增加空間。 */sds sdsMakeRoomFor(sds s, size_t addlen) {    struct sdshdr *sh, *newsh;    size_t free = sdsavail(s);    size_t len, newlen;    /* 如果剩餘的空間足夠儲存addlen,無需增加空間 */    if (free >= addlen) return s;    len = sdslen(s);    sh = (void*) (s-(sizeof(struct sdshdr)));    /* 計算sdshdr地址 */    newlen = (len+addlen); /* 字串增加後的總長度 */    /* 預分配空間的策略,如果新的長度小於 SDS_MAX_PREALLOC(1M),     則預分配多一倍的長度,否則預分配多SDS_MAX_PREALLOC大小的空間 */    if (newlen < SDS_MAX_PREALLOC)        newlen *= 2;    else        newlen += SDS_MAX_PREALLOC;    /* 申請擴充空間 */    newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);    if (newsh == NULL) return NULL;    newsh->free = newlen - len;    return newsh->buf;}


上面函數 sdsMakeRoomFor 裡面會有預分配空間的策略,需要注意的是,預分配空間可以預留空間給字串增加,但同時也會造成浪費。並不是所有的字串都會有預分配空間,只有 sdsgrowzero,sdscatlen,sdscpylen,sdscatfmt 等函數會調用 sdsMakeRoomFor。

本文出自 “chhquan” 部落格,請務必保留此出處http://chhquan.blog.51cto.com/1346841/1769854

redis底層資料結構之sds

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.