標籤:
1.1 Redis字串的特點
1. 二進位安全,字串中間可以包含‘\0‘字元。
2. redis在字串的末尾會添加一個‘\0‘字元,所以redis字串與標準C的字串相容。
3. 根據不同的字串長度使用不同的類型,並且取消編譯過程中的最佳化對齊,節省空間的。
4. 為了提高字串增長的效率,redis在擴充字串長度操作(sdsMakeRoomFor)時的預分配了額外的空間,從而再下一次擴充長度時,不用再次分配空間。注意,redis並沒有在建立字串時分配額外的空間。redis基於一個假設:如果一個字串有了一次擴充長度的操作,那麼它之後很可能也會擴充長度,因此在這時預分配空間。對於redis來說,絕大部分字串是不需要擴充長度的,因此在建立字串時並不預分配空間。
1 struct __attribute__ ((__packed__)) sdshdr5 { 2 unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ 3 char buf[]; 4 }; 5 struct __attribute__ ((__packed__)) sdshdr8 { 6 uint8_t len; /* used */ 7 uint8_t alloc; /* excluding the header and null terminator */ 8 unsigned char flags; /* 3 lsb of type, 5 unused bits */ 9 char buf[];10 };11 struct __attribute__ ((__packed__)) sdshdr16 {12 uint16_t len; /* used */13 uint16_t alloc; /* excluding the header and null terminator */14 unsigned char flags; /* 3 lsb of type, 5 unused bits */15 char buf[];16 };17 struct __attribute__ ((__packed__)) sdshdr32 {18 uint32_t len; /* used */19 uint32_t alloc; /* excluding the header and null terminator */20 unsigned char flags; /* 3 lsb of type, 5 unused bits */21 char buf[];22 };23 struct __attribute__ ((__packed__)) sdshdr64 {24 uint64_t len; /* used */25 uint64_t alloc; /* excluding the header and null terminator */26 unsigned char flags; /* 3 lsb of type, 5 unused bits */27 char buf[];28 };
__attrubte__ ((packed)) 的作用就是告訴編譯器取消結構在編譯過程中的最佳化對齊,按照實際佔用位元組數進行分配。
1.2 實現中使用的技巧 1.2.1 計算結構體的地址
在使用字串的時候,我們並不會經常使用字串結構體(例如sdshdr32),而是會使用其中的字串量(buf),因此需要根據buf找到結構體的地址
1 #define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));2 #define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))3 #define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
1.2.2 根據buf計算flags的地址
利用下標為負數來擷取flags的地址
1 static inline void sdssetalloc(sds s, size_t newlen) { 2 unsigned char flags = s[-1]; 3 switch(flags&SDS_TYPE_MASK) { 4 case SDS_TYPE_5: 5 /* Nothing to do, this type has no total allocation info. */ 6 break; 7 case SDS_TYPE_8: 8 SDS_HDR(8,s)->alloc = newlen; 9 break;10 case SDS_TYPE_16:11 SDS_HDR(16,s)->alloc = newlen;12 break;13 case SDS_TYPE_32:14 SDS_HDR(32,s)->alloc = newlen;15 break;16 case SDS_TYPE_64:17 SDS_HDR(64,s)->alloc = newlen;18 break;19 }20 }
1.2.3 儲存空間預分配
1 sds sdsMakeRoomFor(sds s, size_t addlen) { 2 void *sh, *newsh; 3 size_t avail = sdsavail(s); 4 size_t len, newlen; 5 char type, oldtype = s[-1] & SDS_TYPE_MASK; 6 int hdrlen; 7 8 /* Return ASAP if there is enough space left. */ 9 if (avail >= addlen) return s;10 11 len = sdslen(s);12 sh = (char*)s-sdsHdrSize(oldtype);13 newlen = (len+addlen);14 // 如果小於SDS_MAX_PREALLOC,則分配的新長度為請求長度的2倍15 if (newlen < SDS_MAX_PREALLOC)16 newlen *= 2;17 else18 /* 否則加上SDS_MAX_PREALLOC */19 newlen += SDS_MAX_PREALLOC;20 21 // 根據新長度計算新類型22 type = sdsReqType(newlen);23 24 /* Don‘t use type 5: the user is appending to the string and type 5 is25 * not able to remember empty space, so sdsMakeRoomFor() must be called26 * at every appending operation. */27 if (type == SDS_TYPE_5) type = SDS_TYPE_8;28 29 hdrlen = sdsHdrSize(type);30 if (oldtype==type) {31 // 新舊類型相同,則以原來的為模板重新分配32 newsh = s_realloc(sh, hdrlen+newlen+1);33 if (newsh == NULL) return NULL;34 s = (char*)newsh+hdrlen;35 } else {36 /* Since the header size changes, need to move the string forward,37 * and can‘t use realloc */38 newsh = s_malloc(hdrlen+newlen+1);39 if (newsh == NULL) return NULL;40 memcpy((char*)newsh+hdrlen, s, len+1);41 s_free(sh);42 s = (char*)newsh+hdrlen;43 s[-1] = type;44 sdssetlen(s, len);45 }46 sdssetalloc(s, newlen);47 return s;48 }
redis源碼分析1----字串sds