對于上一篇文章,我又自己總結(jié)歸納并補(bǔ)充了一下,有了第二篇。

          概覽



          <<左移

          開始之前,我們先準(zhǔn)備點(diǎn)東西:位運(yùn)算
          i<<n 總結(jié)為 i*2^n
          所以

          1<<5 = 2^5

          1<<8 = 2^8

          1<<16 = 2^16

          1<<32 = 2^32

          1<<64 = 2^64

          SDS 5種數(shù)據(jù)類型

          Redis 3.2 以后SDS數(shù)據(jù)類型有5個
          #define SDS_TYPE_5 0 #define SDS_TYPE_8 1 #define SDS_TYPE_16 2 #define
          SDS_TYPE_32 3 #define SDS_TYPE_64 4
          結(jié)合上面的位運(yùn)算,我們也能理解這5個數(shù)據(jù)類型的命名規(guī)則。

          外部類型String 找 SDS結(jié)構(gòu)

          我們現(xiàn)在有定義了5種SDS數(shù)據(jù)類型,那么如何根據(jù)字符串長度找這些類型呢?

          或者說輸入的字符串長度和類型有什么關(guān)系?下面我們來看一看他們之間的關(guān)系。



          再來看看源碼:
          static inline char sdsReqType(size_t string_size) { if (string_size < 1<<5)
          return SDS_TYPE_5; if (string_size < 1<<8) return SDS_TYPE_8; if (string_size <
          1<<16) return SDS_TYPE_16; #if (LONG_MAX == LLONG_MAX) if (string_size <
          1ll<<32) return SDS_TYPE_32; return SDS_TYPE_64; #else return SDS_TYPE_32;
          #endif }
          根據(jù)位運(yùn)算左移公式,我可以得知 1<<8 = 2^8 = 256

          那么這里的?。玻担妒侵甘裁矗窟@里的256就是字節(jié)




          也就是說:
          SDS_TYPE_5 -- 32 Byte
          SDS_TYPE_8 -- 256 Byte
          SDS_TYPE_16 -- 64KB
          SDS_TYPE_32 -- ...
          SDS_TYPE_64 -- ...

          現(xiàn)在數(shù)據(jù)類型找到了,我們再來看看比較典型的幾種操作。

           追加字符串

          從使用角度講,追加一般用的頻率很少。所以有多大分配多大。

          所以這里追加的話,有兩種大情況:還有剩余 或 不夠用

          主要講一下不夠用就要重新申請內(nèi)存,那么我們?nèi)绾稳ド暾垉?nèi)存呢?

          這里提供了兩種分配策略:
          <1M ,新空間 = 2倍擴(kuò)容; >1M , 新空間 = 累加1M
          空間有了,那么我們需要根據(jù)最新的空間長度占用,再找到對應(yīng)的新的SDS數(shù)據(jù)類型。



          看一下源碼,增加一下印象:
          /* 追加字符串*/ sds sdscatlen(sds s, const void *t, size_t len) { // 當(dāng)前字符串長度 size_t
          curlen = sdslen(s); // 按需調(diào)整空間(原來字符串,要追加的長度) s = sdsMakeRoomFor(s,len); // 內(nèi)存不足
          if (s == NULL) return NULL; // 追加目標(biāo)字符串到字節(jié)數(shù)組中 memcpy(s+curlen, t, len); //
          設(shè)置追加后的長度 sdssetlen(s, curlen+len); // 追加結(jié)束符 s[curlen+len] = '\0'; return s; }
          /*空間調(diào)整,注意只是調(diào)整空間,后續(xù)自己組裝字符串*/ sds sdsMakeRoomFor(sds s, size_t addlen) { void
          *sh, *newsh; // 當(dāng)前剩下的空間 size_t avail = sdsavail(s); size_t len, newlen; char
          type, oldtype = s[-1] & SDS_TYPE_MASK; int hdrlen; /* 空間足夠 */ if (avail >=
          addlen) return s; // 長度 len = sdslen(s); // 真正的數(shù)據(jù)體 sh =
          (char*)s-sdsHdrSize(oldtype); // 新長度 newlen = (len+addlen); // < 1M 2倍擴(kuò)容 if
          (newlen < SDS_MAX_PREALLOC) newlen *= 2; // > 1M 擴(kuò)容1M else newlen +=
          SDS_MAX_PREALLOC; // 獲取sds 結(jié)構(gòu)類型 type = sdsReqType(newlen); // type5 默認(rèn)轉(zhuǎn)成 type8
          if (type == SDS_TYPE_5) type = SDS_TYPE_8; // 頭長度 hdrlen = sdsHdrSize(type); if
          (oldtype==type) { // 長度夠用 并且 數(shù)據(jù)結(jié)構(gòu)不變 newsh = s_realloc(sh, hdrlen+newlen+1); if
          (newsh == NULL) return NULL; s = (char*)newsh+hdrlen; } else { // 重新申請內(nèi)存 newsh
          = s_malloc(hdrlen+newlen+1); if (newsh == NULL) return NULL;
          memcpy((char*)newsh+hdrlen, s, len+1); s_free(sh); s = (char*)newsh+hdrlen;
          s[-1] = type; sdssetlen(s, len); } sdssetalloc(s, newlen); return s; }
          SDS 和 內(nèi)部類型

          外部字符串類型,找到了SDS結(jié)構(gòu),現(xiàn)在到了SDS轉(zhuǎn)內(nèi)部結(jié)構(gòu)

          對于字符串類型為什么會分 embstr 和 raw呢?

          我們先說一下內(nèi)存分配器:jemalloc、tcmalloc

          這來能為仁兄呢分配內(nèi)存的大小都是 2/4/8/16/32/64 字節(jié)

          對于redis 來講如何利用并適配好內(nèi)存分配器依然需要好好計算一下。

          Redis 給我們實現(xiàn)了很多內(nèi)部數(shù)據(jù)結(jié)構(gòu),這些內(nèi)部數(shù)據(jù)結(jié)構(gòu)得有自己的字描述文件-內(nèi)部結(jié)構(gòu)頭對象
          不同對象有不同的type,同一個對象有不同的存儲形式,還有lru緩存淘汰機(jī)制信息,引用計數(shù)器,指向數(shù)據(jù)體的指針。
          typedef struct redisObject { unsigned type:4; unsigned encoding:4; unsigned
          lru:LRU_BITS; int refcount;       void *ptr; } robj;
          所以SDS和 內(nèi)部類型的關(guān)系類似于這樣的:

          連續(xù)內(nèi)存,和非連續(xù)內(nèi)存



          44 字節(jié)

          SDS為什么會是這樣的兩種內(nèi)部結(jié)構(gòu)呢?
          回憶一下上面提到的:SDS結(jié)構(gòu),最小的應(yīng)該是 SDS_TYPE_8(SDS_TYPE_5默認(rèn)轉(zhuǎn)成8) struc SDS{ int8 capacity;
          // 1字節(jié) int8 len; // 1字節(jié) int8 flags; // 1字節(jié) byte[] content; // 內(nèi)容 }
          所以從上代碼看出,一個最小的SDS,至少占用3字節(jié).
          還有內(nèi)部結(jié)構(gòu)頭:RedisObject typedef struct redisObject { unsigned type:4; // 4bit
          unsigned encoding:4; // 4bit unsigned lru:LRU_BITS; // 24bit int
          refcount;       // 4字節(jié) void *ptr; // 8字節(jié) } robj;
          16字節(jié)?。健?2bit(4字節(jié)) + 4字節(jié) + 8字節(jié)

          所以一個內(nèi)部類型頭指針大小為:16字節(jié)

          再加上最小SDS的3字節(jié),一共 19字節(jié)。也就是說一個最小的字符串所占用的內(nèi)存空間是19字節(jié)

          還記得上面我們提到過的內(nèi)存分配器么?(2/4/8/16/32/64 字節(jié))

          對,如果要給這個最小19字節(jié)分配內(nèi)存,至少要分配一個32字節(jié)的內(nèi)存。當(dāng)然如果字符串長一點(diǎn),再往下就可以分配到64字節(jié)的內(nèi)存。

          以上這種形式被叫做:embstr,這種形式使得 RedisObject和SDS 內(nèi)存地址是連續(xù)的。

          那么一旦大于64字節(jié),形式就變成了raw,這種形式使得內(nèi)存不連續(xù),因為SDS已經(jīng)變大,取得大的連續(xù)內(nèi)存得不償失。

          再回來討論一下 embstr, 最大64字節(jié)內(nèi)存分配下來,我們實際可以真正存儲字符串的長度是多少呢?--44字節(jié)



          64字節(jié),減去RedisObject頭信息19字節(jié),再減去3字節(jié)SDS頭信息,剩下45字節(jié),再去除\0結(jié)尾。這樣最后可以存儲44字節(jié)。

          所以 embstr 形式,可以存儲最大字符串長度是44字節(jié)。

          關(guān)于字符串最大是512M
          Strings Strings are the most basic kind of Redis value. Redis Strings are
          binary safe, this means that a Redis string can contain any kind of data, for
          instance a JPEG image or a serialized Ruby object. A String value can be at max
          512 Megabytes in length.
           出個題(redis 5.0.5版本)

          SET q sc

          encoding:embstr,長度為3



          現(xiàn)在做追加操作,APPEND q scadd ,encoding:raw,長度8


          為什么從 sc ----> scscadd 簡單的追加操作內(nèi)部類型會從 embstr -----> raw ,如何解釋?
          喜歡的歡迎加公眾號或者留言評論探討

          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務(wù)器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點(diǎn)信息
          問題反饋
          郵箱:[email protected]
          QQ群:637538335
          關(guān)注微信

                国产又黄又爽免费视频 | 日批日韩 | 欧美久久精品 | 亚洲日本在线播放 | 日韩一级片在线观看 |