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

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

1.type:數(shù)據(jù)類型標(biāo)識(4 bit)就是redis基本類型
| 類型常量 | 值 | 對應(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ù)類型可對應(yīng)不同底層實現(xiàn):
| 編碼常量 | 值 | 適用類型 | 底層結(jié)構(gòu) |
|---|---|---|---|
| OBJ_ENCODING_INT | 0 | String | 整數(shù)存儲 |
| OBJ_ENCODING_EMBSTR | 1 | String | 短字符串優(yōu)化 |
| OBJ_ENCODING_RAW | 2 | String | SDS動態(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) |
動態(tài)編碼轉(zhuǎn)換示例:
- 當(dāng) Hash 的元素超過
hash-max-ziplist-entries時 OBJ_ENCODING_ZIPLIST→OBJ_ENCODING_HT
3.lru:緩存淘汰信息(24 bit)
- LRU模式:記錄對象最后訪問時間戳(精度:秒級)
- LFU模式(Redis 4.0+):
16 bits 8 bits +------------+------+ | 訪問時間戳 | 頻率 | +------------+------+
- 頻率(logc):基于概率遞增的訪問計數(shù)器
- 時間戳:解決冷數(shù)據(jù)滯留問題
4.refcount:引用計數(shù)(4字節(jié))
- 內(nèi)存回收:
refcount=0時自動釋放內(nèi)存 - 對象共享:相同數(shù)據(jù)復(fù)用對象(如
SET key 100共享整數(shù)對象) - 多客戶端引用:同一 key 被多個客戶端連接引用
5.ptr:數(shù)據(jù)指針(8字節(jié))
指向?qū)嶋H數(shù)據(jù)結(jié)構(gòu),如:
OBJ_ENCODING_INT→ 直接存儲整數(shù)(void *強轉(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ù) 兩個數(shù)據(jù)結(jié)構(gòu)申請了兩片內(nèi)存區(qū)域

EMBSTR編碼

INT編碼

為什么分界線是44字節(jié)?
44字節(jié)的臨界值源于內(nèi)存分配器的優(yōu)化策略,具體計算如下:
1. 內(nèi)存分配器的最小單位
- Redis 默認(rèn)使用 jemalloc 或 glibc malloc
- 這些分配器的最小分配單元通常是 64字節(jié)(CPU緩存行對齊)
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] | 字符串長度 ≤ 44字節(jié) | 字符串長度 > 44字節(jié) |
| 內(nèi)存分配次數(shù) | 1次(RedisObject內(nèi)聯(lián)存儲) | 1次(連續(xù)內(nèi)存塊) | 2次(RedisObject + SDS分開) |
| 適用場景 | 計數(shù)器(如 INCR 操作) | 短字符串(如JSON片段、短URL) | 長文本、二進制數(shù)據(jù) |
| 修改時的行為 | 直接替換整數(shù)值 | 自動轉(zhuǎn)換為 RAW 編碼 | 原地修改或重新分配 |
| 內(nèi)存占用示例 | 存儲 100:16字節(jié)(RedisObject) | 存儲 "hello":16+6=22字節(jié) | 存儲1KB文本:16+1024+9=1049字節(jié) |
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- Redis字符串String操作詳解從基礎(chǔ)到高級應(yīng)用小結(jié)
- SpringBoot3.4.0無法找到StringRedisTemplate?bean的問題Consider?defining?a?bean?of?type?‘org.springframework
- Redis序列化反序列化不一致導(dǎo)致String類型值多了雙引號問題
- Springboot中RedisTemplate設(shè)置String、Hash、List過期時間
- Java中Redis存儲String類型會有亂碼的問題及解決方案
- 一文搞懂Redis最常用String字符串技能
- SpringBoot混合使用StringRedisTemplate和RedisTemplate的坑及解決
相關(guān)文章
redis和redission分布式鎖原理及區(qū)別說明
文章對比了synchronized、樂觀鎖、Redis分布式鎖及Redission鎖的原理與區(qū)別,指出在集群環(huán)境下synchronized失效,樂觀鎖存在數(shù)據(jù)庫性能瓶頸,而Redission通過watchdog自動續(xù)期和Lua原子操作解決Redis鎖的超時問題,推薦其在高并發(fā)場景下的可靠性與易用性2025-08-08
Mac中Redis服務(wù)啟動時錯誤信息:NOAUTH Authentication required
這篇文章主要介紹了Mac中使用Redis服務(wù)啟動時錯誤信息:"NOAUTH Authentication required"問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08
使用RedisAtomicInteger計數(shù)出現(xiàn)少計問題及解決
這篇文章主要介紹了使用RedisAtomicInteger計數(shù)出現(xiàn)少計問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11

