Redis資料類型之字串,redis資料類型

來源:互聯網
上載者:User

Redis資料類型之字串,redis資料類型
Redis資料類型之字串redis的字串

redis的字串不是C語言原生的字串,而是自己構建的稱為簡單動態字串(simple dynamic string),簡稱 SDS,和C語言原生的字串相似,使用’\0’作為結尾。

除了列印日誌之外,我們操作字串基本是在使用SDS

SDS的在redis的主要功能

 1. 儲存資料庫的字串值 2. 用作緩衝區buffer

SDS在redis的定義
在源碼包下面的src目錄下的sds.h 和sds.c

    typedef char *sds;    /**     * 儲存字串對象的結構     */    struct sdshdr {        // buf 中已佔用空間的長度        int len;        // buf 中剩餘可用空間的長度        int free;        // 資料空間        char buf[];    };

為什麼使用SDS而不是C語言原生的字串?
SDS有一下特點

    /**     * 返回實際的長度     */    static inline size_t sdslen(const sds s) {        struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));        return sh->len;    }
    strcat(char* dest,const char* src)

看看sds.c中的sdscat()

    //函數原型    sds sdscat(sds s, const char *t);    //具體實現    sds sdscat(sds s, const char *t) {        return sdscatlen(s, t, strlen(t));    }    //sdscatlen    sds sdscatlen(sds s, const void *t, size_t len) {        struct sdshdr *sh;        // 原有字串長度        size_t curlen = sdslen(s);        // 擴充 sds 空間        // T = O(N)        s = sdsMakeRoomFor(s,len);        // 記憶體不足?直接返回        if (s == NULL) return NULL;        // 複製 t 中的內容到字串後部        // T = O(N)        sh = (void*) (s-(sizeof(struct sdshdr)));        memcpy(s+curlen, t, len);        // 更新屬性        sh->len = curlen+len;        sh->free = sh->free-len;        // 添加新結尾符號        s[curlen+len] = '\0';        // 返回新 sds        return s;    }

可以看到,sds在進行拼接的時候,首先擷取原字串的長度,然後進行擴充。
3. 減少修改字串時的記憶體重新分配
C語言的字串的首先還是依靠字元數組,長度為N的字串對應的是一個長度為N+1的字元數組,所以,在對字串進行操作時,都會發生對字元數組的記憶體重新分配。

對於記憶體的重新分配,一般設計複雜的演算法和系統調用,相對比較耗時,偶爾一次的重新分配還可以接受,但是對於redis,頻繁的操作,肯定效率非常低下。

還記得sds的結構體嗎?儲存了一個未使用的空間。sds通過這個來屏蔽了與底層數組的關聯 。

那麼sds如何使用這個free來實現減少記憶體重新分配?

方法一、記憶體預分配。 最佳化sds的增長操作

簡單的說,就是redis API 在修改sds的時候,如果有涉及對sds長度進行擴充,那麼,同時擴充sds的free空間長度。如果free足夠,那麼就使用free空間。

那麼擴充多少?free與length又有什麼大小關係?
- 如果擴充之後的sds小於1M,那麼,擴充之後的free==length,總的記憶體大小是 free+length+1byte
- 如果擴充之後的sds大于于1M,那麼,free的大小是1M,總長度是1M+length+1byte

方法二、惰性空間釋放 最佳化sds的縮短操作

簡單的說,就是redis API 在截斷sds的時候,程式並不立即回收多出來的記憶體,而是使用free屬性來儲存這些空間。
實現代碼

    sds sdstrim(sds s, const char *cset) {    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));    char *start, *end, *sp, *ep;    size_t len;    // 設定和記錄指標    sp = start = s;    ep = end = s+sdslen(s)-1;    // 修剪, T = O(N^2)    while(sp <= end && strchr(cset, *sp)) sp++;    while(ep > start && strchr(cset, *ep)) ep--;    // 計算 trim 完畢之後剩餘的字串長度    len = (sp > ep) ? 0 : ((ep-sp)+1);    // 如果有需要,前移字串內容    // T = O(N)    if (sh->buf != sp) memmove(sh->buf, sp, len);    // 添加終結符    sh->buf[len] = '\0';    // 更新屬性    sh->free = sh->free+(sh->len-len);    sh->len = len;    // 返回修剪後的 sds    return s;}

二進位安全

C語言的字串只能用來儲存ANSI字元,因為不能包含Null 字元。
所有的sds的api都會以處理二進位的方式來處理儲存在sds位元組數組的資料,不對這些資料進行任何的處理。

相容部分c語言的字串。

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

相關文章

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.