日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747


redis sds 介紹

 

字符串是redis中最為常見的存儲數據存儲類型,其底層實現是簡單的動態字符串sds(simple dynamic string),可以修改的字符串。

sds 介紹

sds本質上是 char *,因為有了表頭sdshdr結構的存在,所以sds比傳統c字符串在某些方面更加優秀,并且能夠兼容傳統C字符串。

sds采用預分配存儲空間的方式來減少內存的頻繁分配,惰性空間釋放的策略來優化sds的縮短操作,降低內存重新分配的概率。

redis 的字符串實現在sds.h sds.c 中。

typedef char *sds;/* Note: sdshdr5 is never used, we just access the flags byte directly. * However is here to document the layout of type 5 SDS strings. */struct __attribute__ ((__packed__)) sdshdr5 {    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */    char buf[];};

上述代碼來自sds.h

  • __attribute__ ((__packed__))redis3.2 之后,針對不同長度的字符串引入了不同的sds數據結構,并且強制內存對齊1,將內存對齊的交給統一的內存分配函數,從而達到節省內存的目的稍微了解c/c++的人都會了解在結構體建立的是時候,會進行字節對齊操作,所以往往比實際變量占用的字節要多一些,如果我們不想要字節對齊怎么辦?在結構體聲明當中__attribute__ ((__packed__))關鍵字可以讓我們的結構體按照緊湊排列的方式,占用內存。如下2種數據結構分別sizeof 將得到不同的結果struct test{ unsigned char flags; int value; }; struct __attribute__ ((__packed__)) test_{ unsigned char flags; int value; }; sizeof(struct test) //size is 8sizeof(struct test_) //size is 5
  • char buf[]char buf[] 等價與 char buf[0] 在標準C和C++中0長數組如char buf[0]是不允許使用的,因為這從語義邏輯上看,是完全沒有意義的。但是,GUN中卻允許使用,而且,很多時候,應用在了變長結構體中。對編譯器來說,此時長度為0的數組并不占用空間,因為數組名本身不占空間,它只是一個偏移量, 數組名這個符號本身代表了一個不可修改的地址常量。我們可以優雅的將buf稱之為柔性數組。在結構中,buf是一個數組名;但該數組沒有元素;該數組的真實地址緊隨結構體sdshdr5之后,而這個地址就是結構體后面數據的地址(如果給這個結構體分配的內容大于這個結構體實際大小,后面多余的部分就是這個buf的內容);這種聲明方法可以巧妙的實現C語言里的數組擴展。對于0長數組的這個特點,很容易構造出變長結構體,如緩沖區,數據包等等struct Buffer { int len; char cData[0]; }; 假如我們要發送1024個字節,我們如何構造這個數據包呢?char *buffer = (char*)malloc(sizeof(Buffer)+1024)我們首先申請1024字節的空間,其次做一個類型轉換如下代碼Buffer *p = (Buffer*)buffer p->len = 1024 memcpy(p.cData,"1024 data............",1024) send(socket,p,sizeof(Buffer)+1024);//發送數據

sds 數據存儲結構

我們摘取其中一個sdshdr32的數據結構來分析redis中sdsh的數據存儲結構

struct __attribute__ ((__packed__)) sdshdr32 {    uint32_t len; /* used */    uint32_t alloc; /* excluding the header and null terminator */    unsigned char flags; /* 3 lsb of type, 5 unused bits */    char buf[];};

sdsnew(const char *init) 會根據init數據的長度去分配內存,分配內存的大小為s_malloc(hdrlen+initlen+1) 其中 hdrlen 為sdshdr* 的結構體的大小,initlen為傳入的init 變量的數據大小或者為sdsnewlen(const void *init, size_t initlen) 傳入的initlen 的大小。

sdsnewlen 方法會根據initlen 的數值去通過sdsReqType去確定type的類型,然后根據返回的type數值再通過sdsHdrSize(type)獲得hdrlen。 具體代碼實現可參見sds.c文件。

當sds s = sdsnew()之后,其中s的位置并不是內存的起始位置, sh = s_malloc(hdrlen+initlen+1), 而是偏移了sh + hdrlen 后的位置 s = (char*)sh+hdrlen。

sds 有一個關鍵的宏SDS_HDR 定義如下

#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))

其中SDS_HDR 能夠將s 的指針位置減去 sdshdr##T的大小,從而將指針位置指向sdsnew內存分配的起始位置dh,進而去通過sh去操作sdshdr##T的成員變量。其中T取值為(5,8,16,32,64)

  • 一個 sds 的內部數據結構
    -----------------------------    | len | alloc | flags | buf |    -----------------------------

如上,其中buf位置真正存儲了字符數據, 前面十三個位置分別存儲了buf相關的sds信息。

  • len記錄當前字節數組的長度(不包括),使得獲取字符串長度的時間復雜度由O(N)變為了O(1)
  • alloc記錄了當前字節數組總共分配的內存大小(不包括)
  • flags記錄了當前字節數組的屬性、用來標識到底是sdshdr8還是sdshdr16等
  • buf保存了字符串真正的值以及末尾的一個

整個sds的內存是連續的,統一開辟的。在大多數操作中,buf內的字符串實體才是操作對象。統一開辟內存能通過buf頭指針進行尋址,拿到整個struct的指針,而且通過buf的頭指針減1直接就能獲取flags的值, flags = s[-1]。

更詳細的sds的分配可參見sds.c中sdsnewlen的實現部分。

分享到:
標簽:redis sds
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定