Redis系列之底層數(shù)據(jù)結(jié)構(gòu)SDS詳解
實(shí)驗(yàn)的環(huán)境
- Redis 6.0
- VSCode 1.88.1
什么是SDS?
SDS:Simple Dynamic String,翻譯為簡(jiǎn)單動(dòng)態(tài)字符串。
SDS是一種用于存儲(chǔ)二進(jìn)制數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu),具有動(dòng)態(tài)擴(kuò)容的特點(diǎn),代碼位于src/sds.h和src/sds.c
SDS的總體數(shù)據(jù)結(jié)構(gòu)大致如圖:在源碼里sds包括幾個(gè)部分,len、alloc、flags、buf,其中 sdshdr是頭部,buf是真實(shí)存儲(chǔ)數(shù)據(jù)的地方,在存儲(chǔ)的數(shù)據(jù)后面會(huì)跟一個(gè)\0,所以數(shù)據(jù)加上\0就是所謂的buf

- len:保存了SDS字符串的長(zhǎng)度
- buf[]:保存數(shù)據(jù)的地方
- alloc:分別以u(píng)int8, uint16, uint32, uint64表示整個(gè)SDS
- flags:始終為一字節(jié), 以低三位標(biāo)示著頭部的類(lèi)型, 高5位未使用
查看源碼sds.h,可以看到SDS里面有幾種不同的頭部,其中sdshdr5實(shí)際并未使用到,所以實(shí)際上有四種不同的頭部
/* Note: sdshdr5 is never used, we just access the flags byte directly.
* However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
uint64_t len; /* used */
uint64_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};為什么要使用SDS?
Redis是用C語(yǔ)言寫(xiě)的,為什么不直接就用C語(yǔ)言里的char來(lái)定義字符串?
獲取字符串長(zhǎng)度
由于有len屬性,所以獲取SDS字符串的長(zhǎng)度只需要讀取len屬性,所以時(shí)間復(fù)雜度為O(1)。
如果直接使用C語(yǔ)言中的字符串來(lái)實(shí)現(xiàn),獲取字符串的長(zhǎng)度需要遍歷計(jì)數(shù),時(shí)間復(fù)雜度為O(n)。
避免緩存區(qū)溢出
在C語(yǔ)言中,如果使用strcat函數(shù)來(lái)進(jìn)行兩個(gè)字符串的拼接,如果沒(méi)有分配足夠長(zhǎng)度的內(nèi)存空間,就會(huì)造成緩存區(qū)溢出。
而對(duì)于SDS數(shù)據(jù)類(lèi)型,在進(jìn)行字符串修改的時(shí)候,會(huì)根據(jù)記錄的len屬性檢查內(nèi)存空間是否滿(mǎn)足需求,如果不滿(mǎn)足,會(huì)進(jìn)行相應(yīng)空間的擴(kuò)展,所以不會(huì)出現(xiàn)緩存區(qū)溢出
減少字符串內(nèi)存重新分配次數(shù)
在C語(yǔ)言中字符串,是不會(huì)記錄字符串的長(zhǎng)度的,所以一旦修改了字符串,就需要重新分配內(nèi)存,因?yàn)槿绻麤](méi)有重新分配,字符串長(zhǎng)度增大時(shí)會(huì)造成內(nèi)存溢出區(qū)溢出,長(zhǎng)度減小時(shí)會(huì)造成內(nèi)存泄漏。
而對(duì)于SDS來(lái)說(shuō),因?yàn)橛虚L(zhǎng)度熟悉len和alloc屬性的存在,SDS實(shí)現(xiàn)了空間預(yù)分配和惰性空間釋放兩種策略來(lái)減少重新分配內(nèi)存
- 空間預(yù)分配:SDS對(duì)空間進(jìn)行擴(kuò)展的時(shí)候,擴(kuò)展的內(nèi)存比實(shí)際需要的多,這樣可以減少字符串增長(zhǎng)操作所需的內(nèi)存重新分配次數(shù)
- 惰性空間釋放:SDS對(duì)字符串進(jìn)行縮短操作時(shí),不會(huì)立即進(jìn)行內(nèi)存重新分配,來(lái)回收縮短后多余的內(nèi)存空間,而是使用
alloc將這些字節(jié)數(shù)量記錄下來(lái),等待后續(xù)使用
二進(jìn)制安全
在C語(yǔ)言中,是以空字符串作為字符串結(jié)束的標(biāo)識(shí),但是一些特殊的字符串,可能就包括空字符串的,所以容易丟失數(shù)據(jù),不能正確存取。
而SDS是根據(jù)len屬性,以處理二進(jìn)制的方式來(lái)處理buf里的數(shù)據(jù),所以保存數(shù)據(jù)更加安全
兼容部分C字符串函數(shù)
SDS可以重用C語(yǔ)言庫(kù)<string.h>中的一部分函數(shù)
C字符串和SDS對(duì)比
| C字符串 | SDS |
|---|---|
| 獲取字符串長(zhǎng)度時(shí)間復(fù)雜度為O(n) | 獲取字符串的長(zhǎng)度時(shí)間復(fù)雜度為O(1) |
| 不安全,可能會(huì)造成緩沖區(qū)溢出 | 安全,不會(huì)造成緩沖區(qū)溢出 |
| 修改字符串n次就需要進(jìn)行n次內(nèi)存分配 | 修改字符串長(zhǎng)度n次,最多需要n次內(nèi)存分配 |
| 只能保存文本數(shù)據(jù) | 可以保存文本數(shù)據(jù)或者二進(jìn)制數(shù)據(jù) |
| 可以使用所有<string.h>庫(kù)中的函數(shù) | 可以使用一部分<string.h>庫(kù)中的函數(shù) |
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Windows操作系統(tǒng)下Redis服務(wù)安裝圖文教程
這篇文章主要介紹了Windows操作系統(tǒng)下Redis服務(wù)安裝圖文教程,文中給大家提供了redis的下載地址,安裝程序步驟,需要的朋友可以參考下2018-03-03
Redis框架在項(xiàng)目中的實(shí)戰(zhàn)
Lettuce是一款高性能、異步非阻塞的 Redis 客戶(hù)端,基于Netty框架開(kāi)發(fā),也是Spring Boot 2.x 及以上版本默認(rèn)的 Redis 客戶(hù)端客戶(hù)端(Lettuce)只需配置哨兵節(jié)點(diǎn)地址和主節(jié)點(diǎn)名稱(chēng),本文介紹Redis框架在項(xiàng)目中的實(shí)戰(zhàn),感興趣的朋友一起看看吧2026-02-02
redis客戶(hù)端實(shí)現(xiàn)高可用讀寫(xiě)分離的方式詳解
基于sentienl 獲取和動(dòng)態(tài)感知 master、slaves節(jié)點(diǎn)信息的變化,我們的讀寫(xiě)分離客戶(hù)端就能具備高可用+動(dòng)態(tài)擴(kuò)容感知能力了,接下來(lái)通過(guò)本文給大家分享redis客戶(hù)端實(shí)現(xiàn)高可用讀寫(xiě)分離的方式,感興趣的朋友一起看看吧2021-07-07
Ubuntu系統(tǒng)中Redis的安裝步驟及服務(wù)配置詳解
本文主要記錄了Ubuntu服務(wù)器中Redis服務(wù)的安裝使用,包括apt安裝和解壓縮編譯安裝兩種方式,并對(duì)安裝過(guò)程中可能出現(xiàn)的問(wèn)題、解決方案進(jìn)行說(shuō)明,以及在手動(dòng)安裝時(shí),服務(wù)器如何添加自定義服務(wù)的問(wèn)題,需要的朋友可以參考下2024-12-12
詳解使用Redis SETNX 命令實(shí)現(xiàn)分布式鎖
本篇文章主要介紹了詳解使用Redis SETNX 命令實(shí)現(xiàn)分布式鎖,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01
使用Redis實(shí)現(xiàn)API網(wǎng)關(guān)或單個(gè)服務(wù)的請(qǐng)求限流的具體代碼
在微服務(wù)架構(gòu)中,對(duì) API 網(wǎng)關(guān)或單個(gè)服務(wù)的請(qǐng)求進(jìn)行速率限制至關(guān)重要,以防止惡意攻擊、資源濫用并確保系統(tǒng)的穩(wěn)定性和可用性,本文將詳細(xì)探討如何利用 Redis 實(shí)現(xiàn) API 網(wǎng)關(guān)或單個(gè)服務(wù)的請(qǐng)求限流,深入分析各種主流算法,需要的朋友可以參考下2025-07-07

