最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Redis中的動態(tài)字符串學(xué)習(xí)教程

 更新時間:2015年08月11日 14:56:45   作者:zinss26914  
這篇文章主要介紹了Redis中的動態(tài)字符串學(xué)習(xí)教程,以sds模塊的使用為主進(jìn)行講解,需要的朋友可以參考下

sds 的用途
Sds 在 Redis 中的主要作用有以下兩個:

實(shí)現(xiàn)字符串對象(StringObject);
在 Redis 程序內(nèi)部用作 char* 類型的替代品;
以下兩個小節(jié)分別對這兩種用途進(jìn)行介紹。

實(shí)現(xiàn)字符串對象

Redis 是一個鍵值對數(shù)據(jù)庫(key-value DB), 數(shù)據(jù)庫的值可以是字符串、集合、列表等多種類型的對象, 而數(shù)據(jù)庫的鍵則總是字符串對象。

對于那些包含字符串值的字符串對象來說, 每個字符串對象都包含一個 sds 值。

“包含字符串值的字符串對象”,這種說法初聽上去可能會有點(diǎn)奇怪, 但是在 Redis 中, 一個字符串對象除了可以保存字符串值之外, 還可以保存 long 類型的值, 所以為了嚴(yán)謹(jǐn)起見, 這里需要強(qiáng)調(diào)一下: 當(dāng)字符串對象保存的是字符串時, 它包含的才是 sds 值, 否則的話, 它就是一個 long 類型的值。
舉個例子, 以下命令創(chuàng)建了一個新的數(shù)據(jù)庫鍵值對, 這個鍵值對的鍵和值都是字符串對象, 它們都包含一個 sds 值:

redis> SET book "Mastering C++ in 21 days"
OK

redis> GET book
"Mastering C++ in 21 days"

以下命令創(chuàng)建了另一個鍵值對, 它的鍵是字符串對象, 而值則是一個集合對象:

redis> SADD nosql "Redis" "MongoDB" "Neo4j"
(integer) 3

redis> SMEMBERS nosql
1) "Neo4j"
2) "Redis"
3) "MongoDB"

用 sds 取代 C 默認(rèn)的 char* 類型

因?yàn)?char* 類型的功能單一, 抽象層次低, 并且不能高效地支持一些 Redis 常用的操作(比如追加操作和長度計(jì)算操作), 所以在 Redis 程序內(nèi)部, 絕大部分情況下都會使用 sds 而不是 char* 來表示字符串。

性能問題在稍后介紹 sds 定義的時候就會說到, 因?yàn)槲覀冞€沒有了解過 Redis 的其他功能模塊, 所以也沒辦法詳細(xì)地舉例說那里用到了 sds , 不過在后面的章節(jié)中, 我們會經(jīng)??吹狡渌K(幾乎每一個)都用到了 sds 類型值。

目前來說, 只要記住這個事實(shí)即可: 在 Redis 中, 客戶端傳入服務(wù)器的協(xié)議內(nèi)容、 aof 緩存、 返回給客戶端的回復(fù), 等等, 這些重要的內(nèi)容都是由 sds 類型來保存的。

redis 中的字符串
在 C 語言中,字符串可以用一個 \0 結(jié)尾的 char 數(shù)組來表示。

比如說, hello world 在 C 語言中就可以表示為 "hello world\0" 。

這種簡單的字符串表示,在大多數(shù)情況下都能滿足要求,但是,它并不能高效地支持長度計(jì)算和追加(append)這兩種操作:

每次計(jì)算字符串長度(strlen(s))的復(fù)雜度為 θ(N) 。
對字符串進(jìn)行 N 次追加,必定需要對字符串進(jìn)行 N 次內(nèi)存重分配(realloc)。
在 Redis 內(nèi)部, 字符串的追加和長度計(jì)算很常見, 而 APPEND 和 STRLEN 更是這兩種操作,在 Redis 命令中的直接映射, 這兩個簡單的操作不應(yīng)該成為性能的瓶頸。

另外, Redis 除了處理 C 字符串之外, 還需要處理單純的字節(jié)數(shù)組, 以及服務(wù)器協(xié)議等內(nèi)容, 所以為了方便起見, Redis 的字符串表示還應(yīng)該是二進(jìn)制安全的: 程序不應(yīng)對字符串里面保存的數(shù)據(jù)做任何假設(shè), 數(shù)據(jù)可以是以 \0 結(jié)尾的 C 字符串, 也可以是單純的字節(jié)數(shù)組, 或者其他格式的數(shù)據(jù)。

考慮到這兩個原因, Redis 使用 sds 類型替換了 C 語言的默認(rèn)字符串表示: sds 既可高效地實(shí)現(xiàn)追加和長度計(jì)算, 同時是二進(jìn)制安全的。

sds 的實(shí)現(xiàn)

在前面的內(nèi)容中, 我們一直將 sds 作為一種抽象數(shù)據(jù)結(jié)構(gòu)來說明, 實(shí)際上, 它的實(shí)現(xiàn)由以下兩部分組成:

typedef char *sds;


struct sdshdr {

  // buf 已占用長度
  int len;

  // buf 剩余可用長度
  int free;

  // 實(shí)際保存字符串?dāng)?shù)據(jù)的地方
  char buf[];
};

其中,類型 sds 是 char * 的別名(alias),而結(jié)構(gòu) sdshdr 則保存了 len 、 free 和 buf 三個屬性。

作為例子,以下是新創(chuàng)建的,同樣保存 hello world 字符串的 sdshdr 結(jié)構(gòu):

struct sdshdr {
  len = 11;
  free = 0;
  buf = "hello world\0"; // buf 的實(shí)際長度為 len + 1
};

通過 len 屬性, sdshdr 可以實(shí)現(xiàn)復(fù)雜度為 θ(1) 的長度計(jì)算操作。

另一方面, 通過對 buf 分配一些額外的空間, 并使用 free 記錄未使用空間的大小, sdshdr 可以讓執(zhí)行追加操作所需的內(nèi)存重分配次數(shù)大大減少, 下一節(jié)我們就會來詳細(xì)討論這一點(diǎn)。

當(dāng)然, sds 也對操作的正確實(shí)現(xiàn)提出了要求 —— 所有處理 sdshdr 的函數(shù),都必須正確地更新 len 和 free 屬性,否則就會造成 bug 。

數(shù)據(jù)類型定義
與sds實(shí)現(xiàn)有關(guān)的數(shù)據(jù)類型有兩個,一個是 sds:

  // 字符串類型的別名 
  typedef char *sds; 


另一個是 sdshdr:

  // 持有sds的結(jié)構(gòu) 
  struct sdshdr { 
    // buf中已經(jīng)被使用的字符串空間數(shù)量 
    int len; 
    // buf中預(yù)留字符串的空間數(shù)量 
    int free; 
    // 實(shí)際存儲字符串的地方 
    char buf[]; 
  }; 


其中,sds只是字符串?dāng)?shù)組類型char*的別名,而sdshdr用于持有和保存sds的信息

比如,sdshdr.len可以用于在O(1)的復(fù)雜度下獲取sdshdr.buf中存儲的字符串的實(shí)際長度,而sdshdr.free則用于保存sdshdr.buf中還有多少預(yù)留空間

(這里sdshdr應(yīng)該是sds handler的縮寫)

將sdshdr用作sds
sds模塊對sdshdr結(jié)構(gòu)使用了一點(diǎn)小技巧:通過指針運(yùn)算,它使得sdshdr結(jié)構(gòu)可以像sds類型一樣被傳值和處理,并在需要的時候恢復(fù)成sdshdr類型

通過下面的函數(shù)定義來理解這個技巧

sdsnewlen 函數(shù)返回一個新的sds值,實(shí)際上,它創(chuàng)建的卻是一個sdshdr結(jié)構(gòu):

  sds sdsnewlen(const void *init, size_t initlen) 
  { 
    struct sdshdr *sh; 
   
    if (init) { 
      // 創(chuàng)建 
      sh = malloc(sizeof(struct sdshdr) + initlen + 1); 
    } else { 
      // 重分配 
      sh = calloc(1, sizeof(struct sdshdr) + initlen + 1); 
    } 
   
    if (sh == NULL) return NULL; 
   
    sh->len = initlen; 
    sh->free = 0;  // 剛開始free為0 
   
    if (initlen && init) { 
      memcpy(sh->buf, init, initlen); 
    } 
    sh->buf[initlen] = '\0'; 
   
    // 只返回sh->buf這個字符串部分 
    return (char *)sh->buf; 
  } 


通過使用變量持有一個sds的值,在遇到那些只處理sds值本身的函數(shù)時,可以直接將sds傳給它們。比如說,sdstoupper 函數(shù)就是其中的一個例子:

 

  static inline size_t sdslen(const sds s) 
  { 
    // 從sds中計(jì)算出相應(yīng)的sdshdr結(jié)構(gòu) 
    struct sdshdr *sh = (void *)(s - (sizeof(struct sdshdr))); 
   
    return sh->len; 
  } 
   
   
  void sdstoupper(sds s) 
  { 
    int len = sdslen(s), j; 
   
    for (j = 0; j < len; j ++) 
      s[j] = toupper(s[j]); 
  } 


這里有一個技巧,通過指針運(yùn)算,可以從sds值中計(jì)算出相應(yīng)的sdshdr結(jié)構(gòu):

sds雖然是指向char *的buf(ps:并且空數(shù)組不占用內(nèi)存空間,數(shù)組名即為內(nèi)存地址),但是分配的時候是分配sizeof(struct sdshdr) + initlen + 1的,通過sds - sizeof(struct sdshdr)可以計(jì)算出struct sdshdr的首地址,從而可以得到len和free的信息

2015811145457425.jpg (476×211)

sdsavail 函數(shù)就是使用這中技巧的一個例子:

 

  static inline size_t sdsavail(const sds s) 
  { 
    struct sdshdr *sh = (void *)(s - (sizeof(struct sdshdr))); 
   
    return sh->free; 
  } 


內(nèi)存分配函數(shù)實(shí)現(xiàn)
和Reids 的實(shí)現(xiàn)決策相關(guān)的函數(shù)是 sdsMakeRoomFor :

 

  sds sdsMakeRoomFor(sds s, size_t addlen) 
  { 
    struct sdshdr *sh, *newsh; 
    size_t free = sdsavail(s); 
    size_t len, newlen; 
   
    // 預(yù)留空間可以滿足本地拼接  
    if (free >= addlen) return s; 
   
    len = sdslen(s); 
    sh = (void *)(s - (sizeof(struct sdshdr))); 
   
    // 設(shè)置新sds的字符串長度 
    // 這個長度比完成本次拼接實(shí)際所需的長度要大 
    // 通過預(yù)留空間優(yōu)化下次拼接操作 
    newlen = (len + addlen); 
    if (newlen < 1024 * 1024) 
      newlen *= 2; 
    else 
      newlen += 1024; 
   
    // 重新分配sdshdr 
    newsh = realloc(sh, sizeof(struct sdshdr) + newlen + 1); 
    if (newsh == NULL) return NULL; 
   
    newsh->free = newlen - len; 
   
    // 只返回字符串部分 
    return newsh->buf; 
  } 


這種內(nèi)存分配策略表明,在對sds 值進(jìn)行擴(kuò)展(expand)時,總會預(yù)留額外的空間,通過花費(fèi)更多的內(nèi)存,減少了對內(nèi)存進(jìn)行重分配(reallocate)的次數(shù),并優(yōu)化下次擴(kuò)展操作的處理速度

再把redis的如果實(shí)現(xiàn)對sds字符串?dāng)U展的方法貼一下,很不錯的思路:

  /** 
   * 按長度len擴(kuò)展sds,并將t拼接到sds的末尾 
   */ 
  sds sdscatlen(sds s, const void *t, size_t len) 
  { 
    struct sdshdr *sh; 
   
    size_t curlen = sdslen(s); 
   
    // O(N) 
    s = sdsMakeRoomFor(s, len); 
    if (s == NULL) return NULL; 
   
    // 復(fù)制 
    memcpy(s + curlen, t, len); 
   
    // 更新len和free屬性 
    sh = (void *)(s - (sizeof(struct sdshdr))); 
    sh->len = curlen + len; 
    sh->free = sh->free - len; 
   
    // 終結(jié)符 
    s[curlen + len] = '\0'; 
   
    return s; 
  } 
   
  /** 
   * 將一個char數(shù)組拼接到sds 末尾 
   */ 
  sds sdscat(sds s, const char *t) 
  { 
    return sdscatlen(s, t, strlen(t)); 
  } 

相關(guān)文章

  • 實(shí)現(xiàn)在線?+?離線模式進(jìn)行遷移?Redis?數(shù)據(jù)實(shí)戰(zhàn)指南

    實(shí)現(xiàn)在線?+?離線模式進(jìn)行遷移?Redis?數(shù)據(jù)實(shí)戰(zhàn)指南

    這篇文章主要介紹了實(shí)現(xiàn)在線?+?離線模式進(jìn)行遷移?Redis?數(shù)據(jù)實(shí)戰(zhàn)指南的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • Windows系統(tǒng)安裝Redis的詳細(xì)圖文教程

    Windows系統(tǒng)安裝Redis的詳細(xì)圖文教程

    但有時候想在windows下折騰下Redis,那么就可以參考下面的方法了,雖然腳本之家小編以前整理了一些,發(fā)現(xiàn)這篇做的比較詳細(xì),下載也給出來了
    2018-08-08
  • Redis的Sentinel解決方案介紹與運(yùn)行機(jī)制

    Redis的Sentinel解決方案介紹與運(yùn)行機(jī)制

    這篇文章主要介紹了Redis的Sentinel解決方案介紹與運(yùn)行機(jī)制, Sentinel 是一款面向分布式服務(wù)架構(gòu)的輕量級流量控制組件,主要以流量為切入點(diǎn),從流量控制、熔斷降級、系統(tǒng)自適應(yīng)保護(hù)等多個維度來保障服務(wù)的穩(wěn)定性,需要的朋友可以參考下
    2023-07-07
  • redis分布式鎖的問題與解決方法

    redis分布式鎖的問題與解決方法

    這篇文章主要給大家介紹了關(guān)于redis分布式鎖的問題與解決方法,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用redis具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • 深入淺析Redis 集群伸縮原理

    深入淺析Redis 集群伸縮原理

    Redis 集群提供了靈活的節(jié)點(diǎn)擴(kuò)容和收縮方案。在不影響集群對外服務(wù)的情況下,可以為集群添加節(jié)點(diǎn)進(jìn)行擴(kuò)容,也可以下線部分節(jié)點(diǎn)進(jìn)行縮容,接下來通過本文給大家分享Redis 集群伸縮原理,感興趣的朋友一起看看吧
    2021-05-05
  • redis數(shù)據(jù)一致性之延時雙刪策略詳解

    redis數(shù)據(jù)一致性之延時雙刪策略詳解

    在使用redis時,需要保持redis和數(shù)據(jù)庫數(shù)據(jù)的一致性,最流行的解決方案之一就是延時雙刪策略,今天我們就來詳細(xì)刨析一下,需要的朋友可以參考下
    2023-09-09
  • redis發(fā)布訂閱模式的實(shí)現(xiàn)

    redis發(fā)布訂閱模式的實(shí)現(xiàn)

    本文主要介紹了redis發(fā)布訂閱模式,Redis?的?SUBSCRIBE?命令可以讓客戶端訂閱任意數(shù)量的頻道,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-04-04
  • Redis分布式鎖之紅鎖的實(shí)現(xiàn)

    Redis分布式鎖之紅鎖的實(shí)現(xiàn)

    在Redis中,紅鎖是一種分布式鎖的實(shí)現(xiàn)機(jī)制,旨在解決多個客戶端在分布式環(huán)境中對共享資源進(jìn)行并發(fā)訪問的問題,本文主要介紹了Redis分布式鎖之紅鎖的實(shí)現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • Redis實(shí)現(xiàn)分布式鎖的幾種方法總結(jié)

    Redis實(shí)現(xiàn)分布式鎖的幾種方法總結(jié)

    這篇文章主要介紹了Redis實(shí)現(xiàn)分布式鎖的幾種方法總結(jié)的相關(guān)資料, Redis實(shí)現(xiàn)與Zookeeper實(shí)現(xiàn)和數(shù)據(jù)庫實(shí)現(xiàn),需要的朋友可以參考下
    2017-07-07
  • Redis RDB技術(shù)底層原理詳解

    Redis RDB技術(shù)底層原理詳解

    為了使Redis在重啟之后仍能保證數(shù)據(jù)不丟失,需要將數(shù)據(jù)從內(nèi)存中以某種形式同步到硬盤中,這一過程就是持久化,本文重點(diǎn)給大家介紹Redis RDB技術(shù)底層原理實(shí)現(xiàn)方法,一起看看吧
    2021-09-09

最新評論

大新县| 鄱阳县| 常宁市| 伊春市| 襄樊市| 绥宁县| 襄樊市| 沁水县| 巢湖市| 玛纳斯县| 航空| 偏关县| 红河县| 遵义市| 襄城县| 乌鲁木齐县| 阳江市| 马鞍山市| 甘泉县| 光泽县| 水富县| 涿州市| 大渡口区| 昌黎县| 历史| 莱阳市| 云梦县| 平乡县| 威远县| 吴忠市| 巫山县| 诸暨市| 普陀区| 基隆市| 札达县| 东源县| 五台县| 茌平县| 额济纳旗| 山丹县| 博白县|