知識點
[注] 相關源碼保存位置:src/redis.h、src/sds.h、src/sds.c。
-
在 redis 中字符串叫 SDS(Simple Dynamic String,動態字符串)
-
SDS 結構如下 – 是一個帶長度信息的字節數組。
struct SDS<T> { T capacity; // 數組容量,1byte T len; // 數組長度,1byte byte flags; // 特殊標識位,1byte byte[] content; // 數組內容 }
-
字符串存儲方式分爲
- embstr 長度 44byte 及以下
- raw 長度 45byte 及以上
-
embstr 最大長度爲 44byte 的原因如下
- 所有的 Redis 對象都有下面的這個結構頭,此結構佔據 16byte 的存儲空間
struct RedisObject { int4 type; // 4bits int4 encoding; // 4bits int24 lru; // 24bits int32 refcount; // 4bytes void *ptr; // 8bytes,64-bit system } robj;
- 此外 SDS 結構體至少要佔用
3byte
的空間,所以分配一個字符串的最小空間佔用爲 19byte (16+3) - 內存分配器 jemalloc/tcmalloc 等分配內存大小的單位都是 2、4、8、16、32、64 等等,爲了能容納一個完整的 embstr 對象,jemalloc 最少會分配 32byte 的空間,如果字符串再稍微長一點,那就是 64byte 的空間。如果總體超出了 64byte,Redis 認爲它是一個大字符串,不再使用 emdstr 形式存儲,而該用 raw 形式。
- SDS 結構體中的 content 中的字符串是以字節
\0
結尾的字符串,之所以多出這樣一個字節,是爲了便於直接使用 glibc 的字符串處理函數,以及爲了便於字符串的調試打印輸出。
所以 embstr 最大能容納的字符串長度就是 44byte (64-16-3-1)。
- 所有的 Redis 對象都有下面的這個結構頭,此結構佔據 16byte 的存儲空間
擴容策略
- 字符串在長度小於 1M 之前,擴容空間採用加倍策略,也就是保留 100% 的冗餘空間。
- 當長度超過 1M 之後,爲了避免加倍後的冗餘空間過大而導致浪費,每次擴容只會多分配 1M 大小的冗餘空間。
SDS API
函數 | 作用 | 時間複雜度 |
---|---|---|
sdsnew | 創建一個給定包含C字符串的SDS | O(N),N爲給定C字符串的長度 |
sdsempty | 創建一個不包含任何內容的空 SDS | O(1) |
sdsfree | 釋放給定的SDS | O(N),N爲被釋放的SDS的長度 |
sdslen | 返回SDS已使用的字節數 可以直接去讀取SDS的len屬性 | O(1) |
sdsavail | 返回SDS未使用的字節數 可以直接去讀取SDS的free屬性 | O(1) |
sdsdup | 創建一個給定SDS的副本(copy) | O(N),N爲給定SDS的長度 |
sdsclear | 清空給定的SDS字符串內容 使用惰性空間釋放策略 | O(1) |
sdscat | 將給定的C字符串拼接到SDS字符串的末尾 | O(N),N爲拼接C字符串的長度 |
sdscatsds | 將給定的SDS字符串拼接到另一個SDS字符串的末尾 | O(N),N爲拼接SDS字符串的長度 |
sdscpy | 將給定的C字符串複製到SDS中,覆蓋原來SDS中的字符串 | O(N),N爲被複制C字符串的長度 |
sdsgrowzero | 用空字符串將SDS擴展到指定的長度 | O(N),N被擴展的新增字節數 |
sdsrange | 保留SDS給定區域的數據,不在區域的數據覆蓋或者清除 | O(N),N被保留的字節數 |
sdstrim | 接受一個SDS和C字符串的參數,從SDS中移除所有在C字符串中出現過的字符 | O(N*N),N爲給定C字符串的長度 |
sdscmp | 對比兩個SDS是否相等 | O(N),N兩個SDS中len較小的值 |