文章目錄
- 什么是SDS(簡單動態字符串)
- SDS結構
- SDS的優點
- O(1) 時間復雜度獲取字符串長度
- 避免緩沖區溢出
- 減少內存重分配次數
- 二進制安全
- 兼容C語言字符串函數
- SDS的操作
- 總結
什么是SDS(簡單動態字符串)
redis是由C語言編寫的,但是redis在實現字符串中并沒有采用傳統C語言中的字符串表示(傳統的C語言字符串是一個以空字符‘\0’
結尾的字符數組char*),而是自己定義了一種叫做簡單動態字符串(simple dynamic string, 簡稱SDS)的抽象類型,并用SDS用作redis默認的字符串表示。
SDS結構
Redis 5.0 及之后的版本
struct __attribute__ ((__packed__)) sdshdr {uint8_t len; // 字符串的實際長度uint8_t alloc; // 分配的總空間(不包括頭部和結尾的 \0)unsigned char flags; // 標志位,用于標識 SDS 的類型char buf[]; // 存儲字符串內容的柔性數組
};
-
len
:記錄字符串的實際長度(不包括結尾的\0
)。 -
alloc
:記錄分配的總空間(不包括頭部和結尾的\0
)。 -
flags
:標志位,用于標識 SDS 的類型(如SDS_TYPE_8
、SDS_TYPE_16
等)。 -
buf
:存儲字符串內容的動態數組,末尾會自動添加\0
,以兼容 C 語言的字符串函數。
新版SDS根據字符串的長度動態選擇不同的類型:
-
SDS_TYPE_5
:長度小于 32。 -
SDS_TYPE_8
:長度小于 256。 -
SDS_TYPE_16
:長度小于 65536。 -
SDS_TYPE_32
:長度小于 2^32。 -
SDS_TYPE_64
:長度小于 2^64。
這種設計可以根據字符串的實際長度動態調整頭部的大小,進一步節省內存。
SDS的優點
O(1) 時間復雜度獲取字符串長度
C 語言的原生字符串需要通過遍歷(strlen
)來獲取長度,時間復雜度為 O(n)。
SDS 直接通過 len
字段獲取長度,時間復雜度為 O(1)。
避免緩沖區溢出
C 語言的原生字符串操作(如 strcat
)容易導致緩沖區溢出。
// 將 src 字符串拼接到 dest 字符串后面
char *strcat(char *dest, const char* src);
C 語言的字符串是不會記錄自身緩沖區大小的,所以 strcat 函數假定程序員在執行這個函數時,已經為 dest 分配了足夠多的內存,可以容納 src 字符串中的所有內容,而一旦這個假定不成立,就會發生緩沖區溢出可能會造成程序運行終止。
SDS 在修改字符串時會檢查剩余空間(free
),如果不足則自動擴展,避免溢出。
減少內存重分配次數
SDS 采用預分配空間的策略:
- 當字符串長度增加時,SDS 會額外分配一些未使用的空間(
free
),減少后續修改時的內存重分配次數。 - 例如,SDS 的擴容策略通常是:新長度小于 1MB 時,分配雙倍空間;大于 1MB 時,額外分配 1MB。
二進制安全
C 語言的原生字符串以 \0
作為結束符,不能存儲包含 \0
的二進制數據。因此其只能保存文本數據,不能保存圖片,音頻,視頻和壓縮文件等二進制數據,否則可能出現字符串不完整的問題,所以其是二進制不安全。
SDS 通過 len
字段記錄長度,可以存儲任意二進制數據,包括 \0
。
兼容C語言字符串函數
SDS 的 buf
字段末尾會自動添加 \0
,因此可以直接使用 C 語言的字符串函數(如 printf
)。
SDS的操作
Redis 提供了一系列函數來操作 SDS,例如:
- 創建 SDS:
sdsnew
、sdsempty
- 追加字符串:
sdscat
- 復制字符串:
sdscpy
- 釋放 SDS:
sdsfree
- 獲取長度:
sdslen
- 獲取剩余空間:
sdsavail
總結
SDS 是 Redis 中用于表示字符串的底層數據結構,具有高效、安全、靈活的特點。它通過預分配空間、記錄長度等方式,避免了 C 語言原生字符串的缺陷,同時兼容 C 語言的字符串函數。SDS 的設計是 Redis 高性能和高可靠性的重要基礎之一。