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é)(分配器最小單元)
- 固定開(kāi)銷: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分開(kāi)) |
| 適用場(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實(shí)現(xiàn)的分布式唯一編號(hào)生成工具類
這篇文章主要介紹了基于Redis實(shí)現(xiàn)的分布式唯一編號(hào)生成工具類,核心功能是生成格式為 業(yè)務(wù)編碼+日期+3位自增序號(hào)(如 JJ20250826001)的全局唯一編號(hào),適用于分布式系統(tǒng)中需要有序、不重復(fù)編號(hào)的場(chǎng)景(如訂單號(hào)、單據(jù)號(hào)等),以下是詳細(xì)解析,需要的朋友可以參考下2025-11-11
Redis集群水平擴(kuò)展、集群中添加以及刪除節(jié)點(diǎn)的操作
這篇文章主要介紹了Redis集群水平擴(kuò)展、集群中添加以及刪除節(jié)點(diǎn)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-03-03
redis.clients.jedis.exceptions.JedisDataException:?NOAUTH?
本文主要介紹了redis.clients.jedis.exceptions.JedisDataException:?NOAUTH?Authentication?required數(shù)據(jù)操作異常的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05
Redis核心原理與實(shí)踐之字符串實(shí)現(xiàn)原理
這本書(shū)深入地分析了Redis常用特性的內(nèi)部機(jī)制與實(shí)現(xiàn)方式,內(nèi)容源自對(duì)Redis源碼的分析,并從中總結(jié)出設(shè)計(jì)思路、實(shí)現(xiàn)原理。對(duì)Redis字符串實(shí)現(xiàn)原理相關(guān)知識(shí)感興趣的朋友一起看看吧2021-09-09
Redis+Caffeine實(shí)現(xiàn)高效兩級(jí)緩存架構(gòu)的詳細(xì)指南
在現(xiàn)代高并發(fā)系統(tǒng)中,緩存是提升系統(tǒng)性能的關(guān)鍵組件之一,本文將介紹如何結(jié)合 Redis 和 Caffeine 構(gòu)建一個(gè)高效的兩級(jí)緩存系統(tǒng),需要的小伙伴可以了解下2025-07-07

