網站首頁 編程語言 正文
正文
Redis 沒有直接使用 C 語言傳統的字符串表示(而是以空字符結尾的字符數組,以下簡稱 C 字符串),自己構建了一種名為簡單動態字符串(simple dynamic string,SDS) 的抽象類型,并將 SDS 用作 Redis 的默認字符串表示。
在 Redis 里面,C 字符串只會作為字符串字面量(string literal),用在一些無須對字符串值進行修改的地方,比如打印日志:
redisLog(REDIS_WARNING,"Redis is now ready to exit, bye bye...");
當 Redis 需要的不僅僅是一個字符串字面量,而是一個可以被修改的字符串值時,Redis 就會使用 SDS 來表示字符串值:比如在 Redis 的數據庫里面,包含字符串的鍵值對在底層都是由 SDS 實現的。
舉個例子,如果客戶端執行命令:
redis> SET msg "hello world" OK
那么 Redis 將在數據庫中創建了一個新的鍵值對,其中:
- 鍵值對的鍵是一個字符串對象,對象的底層實現是一個保存著字符串
"msg"
的 SDS 。 - 鍵值對的值也是一個字符串對象,對象的底層實現是一個保存著字符串
"hello world"
的SDS。
又比如說,如果客戶端執行命令:
redis> RPUSH fruits "apple" "banana" "cherry" (integer) 3
那么 Redis 將在數據庫中創建一個新的鍵值對,其中:
- 鍵值對的鍵是一個字符串對象,對象的底層實現是一個保存了字符串
"fruits"
的 SDS 。 - 鍵值對的值是一個列表對象,列表對象包含了三個字符串對象,這三個字符串對象分別由三個 SDS 實現:第一個 SDS 保存著字符串
"apple"
,第二個 SDS 保存著字符串"banana"
,第三個 SDS 保存著字符串"cherry"
。
除了用來保存數據庫中的字符串值之外,SDS 還被用作緩沖區(buffer):AOF 模塊中的 AOF 緩沖區,以及客戶端狀態中的輸入緩沖區,都是由 SDS 實現的,在之后介紹 AOF 持久化和客戶端狀態的時候,我們會看到 SDS 在這兩個模塊中的應用。
AOF中記錄的是每一個命令的詳細信息,包括完整的命令類型、參數等。只要產生寫命令,就會實時寫入到AOF文件中
SDS的定義
struct sdshdr { // 記錄 buf 數組中已使用字節的數量 int len; // 記錄 buf 數組中未使用字節的數量 int free; // 字節數組,用于保存字符串 char buf[]; };
與C字符串的區別
C語言使用長度為 N+1
的字符數組來表示長度為 N
的字符串,并且字符數組的最后一個元素總是空字符 '\0'
。
獲取字符串長度
因為 C 字符串并不記錄自身的長度信息,所以為了獲取一個 C 字符串的長度,程序必須遍歷整個字符串O(N)O(N)O(N) 。
和 C 字符串不同,因為 SDS 在 len
屬性中記錄了 SDS 本身的長度,所以獲取一個 SDS 長度的復雜度僅為 O(1)O(1)O(1) 。
杜絕緩沖區溢出
C 字符串不記錄自身長度帶來的另一個問題是容易造成緩沖區溢出(buffer overflow)。
假設程序里有兩個在內存中緊鄰著的 C 字符串 s1 和 s2 ,其中 s1 保存了字符串 "Redis" ,而 s2 則保存了字符串 "MongoDB" .
如果一個程序員決定通過執行:strcat(s1, "Cluster")
將 s1 的內容修改為 "Redis Cluster" ,但粗心的他卻忘了在執行 strcat
之前為 s1 分配足夠的空間,那么在 strcat 函數執行之后,s1 的數據將溢出到 s2 所在的空間中,導致 s2 保存的內容被意外地修改。
SDS 的空間分配策略完全杜絕了發生緩沖區溢出的可能性:當 SDS API 需要對 SDS 進行修改時,API 會先檢查 SDS 的空間是否滿足修改所需的要求,如果不滿足的話,API 會自動將 SDS 的空間擴展至執行修改所需的大小,然后才執行實際的修改操作,所以使用 SDS 既不需要手動修改 SDS 的空間大小,也不會出現前面所說的緩沖區溢出問題。
減少內存分配次數
因為 C 字符串的長度和底層數組的長度之間存在著這種關聯性,所以每次增長或者縮短一個 C 字符串,程序都總要對保存這個 C 字符串的數組進行一次內存重分配操作,在 SDS 中,數組里面可以包含未使用的字節,而這些字節的數量就由 SDS 的 free
屬性記錄。并實現了兩種優化策略:
空間預分配,當進行字符串增長操作時,程序會額外分配空間,并記錄的free
字段
比如原長度為8的字符串,新增5個長度后,總共為13長度,則預分配13+13+1=27字節(額外一字節用于保存空字符串)
對于大于1M來說,分配空間為原有總長度+1MB+1byte
比如增加完字符串后長度為15MB,則為15MB+1MB+1byte
惰性空間釋放,當進行字符串縮短操作時,程序不立即重新分配內存,而是用free
屬性將這些字節的數量記錄起來。
二進制安全
C字符串中不能包含空字符串,否則會被誤認為是字符串結尾。所有 SDS API 都會以處理二進制的方式來處理 SDS 存放在 buf
數組里的數據,程序不會對其中的數據做任何限制、過濾、或者假設 ——數據在寫入時是什么樣的,它被讀取時就是什么樣。
原文鏈接:https://juejin.cn/post/7193517801909747771
相關推薦
- 2022-08-17 python運行腳本文件的三種方法實例_python
- 2022-04-25 C語言的結構體你了解嗎_C 語言
- 2023-12-15 log4j.properties自定義日志配置
- 2022-10-19 Python?Pandas?修改表格數據類型?DataFrame?列的順序案例_python
- 2022-07-13 Golang實現常見排序算法的示例代碼_Golang
- 2023-03-28 Python中的len()函數是什么意思_python
- 2022-11-14 Python語言中Tuple的由來分析_python
- 2024-01-29 理解并使用 XPath 中的 `normalize-space` 函數
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支