redis數(shù)據(jù)結(jié)構(gòu)之String詳解
一、為什么Redis選String作為基礎(chǔ)類型?
redis中的所有key是字符串,所有value本質(zhì)上也是字符串,比如 集合set中的每一個(gè) 成員 都是一個(gè)獨(dú)立的字符串對(duì)象,列表中的每一個(gè) 元素 都是一個(gè)獨(dú)立的字符串對(duì)象,整個(gè)HASH是一個(gè)對(duì)象,它內(nèi)部的每一個(gè) 字段(field) 和一個(gè)字段值(value) 都是一個(gè)獨(dú)立的字符串對(duì)象
redis是通過(guò)c語(yǔ)言來(lái)實(shí)現(xiàn)的,但是沒(méi)有直接使用c語(yǔ)言中的字符串,有幾下幾點(diǎn)原因
- 獲取字符串長(zhǎng)度需要通過(guò)運(yùn)算:C字符串以
\0(空字符)結(jié)尾,要獲取長(zhǎng)度必須遍歷整個(gè)數(shù)組直到遇到\0,時(shí)間復(fù)雜度為O(n),這在高性能數(shù)據(jù)庫(kù)如Redis中效率低下。 - 非二進(jìn)制安全:C字符串不能存儲(chǔ)任意二進(jìn)制數(shù)據(jù),因?yàn)樗蕾囉?code>\0作為結(jié)束符。如果數(shù)據(jù)中包含
\0(如一些二進(jìn)制文件),會(huì)被錯(cuò)誤截?cái)?,破壞?shù)據(jù)完整性。 - 不可修改:C語(yǔ)言字符串常量(如
char* s = "hello")是只讀的,無(wú)法直接擴(kuò)展或修改其長(zhǎng)度,這在動(dòng)態(tài)數(shù)據(jù)存儲(chǔ)中不靈活。
Redis的解決方案:Redis因此構(gòu)建了自己的字符串結(jié)構(gòu)——SDS(簡(jiǎn)單動(dòng)態(tài)字符串),它通過(guò)設(shè)計(jì)一個(gè)智能結(jié)構(gòu)來(lái)支持查找、二進(jìn)制安全性和動(dòng)態(tài)修改。
二、SDS底層數(shù)據(jù)結(jié)構(gòu)

uint8_t (8位無(wú)符號(hào)整數(shù)),可表示的最大值是 255 (因?yàn)?2^8 - 1 = 255),因此 len 最多記錄 255 字節(jié) 的長(zhǎng)度,否則會(huì)溢出,如果一個(gè) SDS 字符串的實(shí)際長(zhǎng)度超過(guò) 255 字節(jié),Redis 會(huì)自動(dòng)選擇更大容量的結(jié)構(gòu)體(如 sdshdr16/sdshdr32)。
三、RedisObject是什么
通常我們了解的數(shù)據(jù)結(jié)構(gòu)有字符串、雙端鏈表、字典、壓縮列表、整數(shù)集合等,但是Redis為了加快讀寫速度,并沒(méi)有直接使用這些數(shù)據(jù)結(jié)構(gòu),而是在此基礎(chǔ)上又包裝了一層稱之為RedisObject。
RedisObject 有五種對(duì)象:字符串對(duì)象(String)、列表對(duì)象(List)、哈希對(duì)象(Hash)、集合對(duì)象(Set)和有序集合對(duì)象(ZSet)。

1.type:數(shù)據(jù)類型標(biāo)識(shí)(4 bit)就是redis基本類型
| 類型常量 | 值 | 對(duì)應(yīng)數(shù)據(jù)結(jié)構(gòu) |
|---|---|---|
| OBJ_STRING | 0 | 字符串 |
| OBJ_LIST | 1 | 列表 |
| OBJ_SET | 2 | 集合 |
| OBJ_ZSET | 3 | 有序集合 |
| OBJ_HASH | 4 | 哈希表 |
2.encoding:內(nèi)部編碼(4 bit)
同一數(shù)據(jù)類型可對(duì)應(yīng)不同底層實(shí)現(xiàn):
| 編碼常量 | 值 | 適用類型 | 底層結(jié)構(gòu) |
|---|---|---|---|
| OBJ_ENCODING_INT | 0 | String | 整數(shù)存儲(chǔ) |
| OBJ_ENCODING_EMBSTR | 1 | String | 短字符串優(yōu)化 |
| OBJ_ENCODING_RAW | 2 | String | SDS動(dòng)態(tài)字符串 |
| OBJ_ENCODING_HT | 3 | Hash/Set | 哈希表 |
| OBJ_ENCODING_ZIPLIST | 4 | List/Hash/Zset | 壓縮列表 |
| OBJ_ENCODING_QUICKLIST | 5 | List | 快速列表 |
| OBJ_ENCODING_SKIPLIST | 6 | Zset | 跳表 |
| OBJ_ENCODING_STREAM | 7 | Stream | 流數(shù)據(jù)結(jié)構(gòu) |
動(dòng)態(tài)編碼轉(zhuǎn)換示例:
- 當(dāng) Hash 的元素超過(guò)
hash-max-ziplist-entries時(shí) OBJ_ENCODING_ZIPLIST→OBJ_ENCODING_HT
3.lru:緩存淘汰信息(24 bit)
- LRU模式:記錄對(duì)象最后訪問(wèn)時(shí)間戳(精度:秒級(jí))
- LFU模式(Redis 4.0+):
16 bits 8 bits +------------+------+ | 訪問(wèn)時(shí)間戳 | 頻率 | +------------+------+
- 頻率(logc):基于概率遞增的訪問(wèn)計(jì)數(shù)器
- 時(shí)間戳:解決冷數(shù)據(jù)滯留問(wèn)題
4.refcount:引用計(jì)數(shù)(4字節(jié))
- 內(nèi)存回收:
refcount=0時(shí)自動(dòng)釋放內(nèi)存 - 對(duì)象共享:相同數(shù)據(jù)復(fù)用對(duì)象(如
SET key 100共享整數(shù)對(duì)象) - 多客戶端引用:同一 key 被多個(gè)客戶端連接引用
5.ptr:數(shù)據(jù)指針(8字節(jié))
指向?qū)嶋H數(shù)據(jù)結(jié)構(gòu),如:
OBJ_ENCODING_INT→ 直接存儲(chǔ)整數(shù)(void *強(qiáng)轉(zhuǎn)為long)OBJ_ENCODING_RAW→ 指向sds結(jié)構(gòu)OBJ_ENCODING_HT→ 指向dict哈希表
四、String類型數(shù)據(jù)結(jié)構(gòu)
string類型在redis中有三種編碼方式
RAW編碼
分配兩次內(nèi)存 RedisObject和SDS的內(nèi)存不連續(xù) 兩個(gè)數(shù)據(jù)結(jié)構(gòu)申請(qǐng)了兩片內(nèi)存區(qū)域

EMBSTR編碼

INT編碼

為什么分界線是44字節(jié)?
44字節(jié)的臨界值源于內(nèi)存分配器的優(yōu)化策略,具體計(jì)算如下:
1. 內(nèi)存分配器的最小單位
- Redis 默認(rèn)使用 jemalloc 或 glibc malloc
- 這些分配器的最小分配單元通常是 64字節(jié)(CPU緩存行對(duì)齊)
2. EMBSTR 的總內(nèi)存占用公式
總大小 = RedisObject(16字節(jié)) + SDS頭部(3字節(jié)) + 字符串內(nèi)容(N字節(jié)) + 結(jié)束符\0(1字節(jié))
- 最大允許占用:64字節(jié)(分配器最小單元)
- 固定開銷:16(robj) + 3(sds) + 1(\0) = 20字節(jié)
- 可用空間:64 - 20 = 44字節(jié)
| 編碼類型 | OBJ_ENCODING_INT | OBJ_ENCODING_EMBSTR | OBJ_ENCODING_RAW |
|---|---|---|---|
| 觸發(fā)條件 | 數(shù)值類型且值在 [LONG_MIN, LONG_MAX] | 字符串長(zhǎng)度 ≤ 44字節(jié) | 字符串長(zhǎng)度 > 44字節(jié) |
| 內(nèi)存分配次數(shù) | 1次(RedisObject內(nèi)聯(lián)存儲(chǔ)) | 1次(連續(xù)內(nèi)存塊) | 2次(RedisObject + SDS分開) |
| 適用場(chǎng)景 | 計(jì)數(shù)器(如 INCR 操作) | 短字符串(如JSON片段、短URL) | 長(zhǎng)文本、二進(jìn)制數(shù)據(jù) |
| 修改時(shí)的行為 | 直接替換整數(shù)值 | 自動(dòng)轉(zhuǎn)換為 RAW 編碼 | 原地修改或重新分配 |
| 內(nèi)存占用示例 | 存儲(chǔ) 100:16字節(jié)(RedisObject) | 存儲(chǔ) "hello":16+6=22字節(jié) | 存儲(chǔ)1KB文本:16+1024+9=1049字節(jié) |
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- Redis字符串String操作詳解從基礎(chǔ)到高級(jí)應(yīng)用小結(jié)
- SpringBoot3.4.0無(wú)法找到StringRedisTemplate?bean的問(wèn)題Consider?defining?a?bean?of?type?‘org.springframework
- Redis序列化反序列化不一致導(dǎo)致String類型值多了雙引號(hào)問(wèn)題
- Springboot中RedisTemplate設(shè)置String、Hash、List過(guò)期時(shí)間
- Java中Redis存儲(chǔ)String類型會(huì)有亂碼的問(wèn)題及解決方案
- 一文搞懂Redis最常用String字符串技能
- SpringBoot混合使用StringRedisTemplate和RedisTemplate的坑及解決
相關(guān)文章
redis和redission分布式鎖原理及區(qū)別說(shuō)明
文章對(duì)比了synchronized、樂(lè)觀鎖、Redis分布式鎖及Redission鎖的原理與區(qū)別,指出在集群環(huán)境下synchronized失效,樂(lè)觀鎖存在數(shù)據(jù)庫(kù)性能瓶頸,而Redission通過(guò)watchdog自動(dòng)續(xù)期和Lua原子操作解決Redis鎖的超時(shí)問(wèn)題,推薦其在高并發(fā)場(chǎng)景下的可靠性與易用性2025-08-08
Mac中Redis服務(wù)啟動(dòng)時(shí)錯(cuò)誤信息:NOAUTH Authentication required
這篇文章主要介紹了Mac中使用Redis服務(wù)啟動(dòng)時(shí)錯(cuò)誤信息:"NOAUTH Authentication required"問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
Redis報(bào)錯(cuò):無(wú)法連接Redis服務(wù)的解決方法
在Linux系統(tǒng)上運(yùn)行Redis服務(wù)時(shí),有時(shí)會(huì)遇到“無(wú)法連接Redis服務(wù)”的報(bào)錯(cuò),本文就詳細(xì)的介紹一下解決方法,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
使用RedisAtomicInteger計(jì)數(shù)出現(xiàn)少計(jì)問(wèn)題及解決
這篇文章主要介紹了使用RedisAtomicInteger計(jì)數(shù)出現(xiàn)少計(jì)問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
redis?zset實(shí)現(xiàn)滑動(dòng)窗口限流的代碼
這篇文章主要介紹了redis?zset實(shí)現(xiàn)滑動(dòng)窗口限流,滑動(dòng)窗口算法思想就是記錄一個(gè)滑動(dòng)的時(shí)間窗口內(nèi)的操作次數(shù),操作次數(shù)超過(guò)閾值則進(jìn)行限流,本文通過(guò)實(shí)例代碼給大家詳細(xì)介紹,需要的朋友參考下吧2022-03-03
Redis不同數(shù)據(jù)類型使用場(chǎng)景代碼實(shí)例
這篇文章主要介紹了Redis不同數(shù)據(jù)類型使用場(chǎng)景代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12
使用Redis實(shí)現(xiàn)延時(shí)任務(wù)的解決方案
這篇文章主要介紹了使用Redis實(shí)現(xiàn)延時(shí)任務(wù)的解決方案,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-08-08

