redis 类型底层存储结构

Redis数据结构与存储机制
本文深入探讨Redis内部数据结构,包括redisObject对象、不同数据类型的存储方式、虚拟内存功能,以及过期数据的清除策略。同时,详细解析String、Hash、Set、SortedSet和List等数据类型的内部实现。

1、数据结构 

Redis 内部使用一个 redisObject 对象来表示所有的 key 和 value。

a、type :代表一个 value 对象具体是何种数据类型。

b、encoding :是不同数据类型在 redis 内部的存储方式,比如:type=string 代表 value 存储的是一个普通字符串,那么对应的 encoding 可以是 raw 或者是 int,如果是 int 则代表实际 redis 内部是按数值型类存储和表示这个字符串的,当然前提是这个字符串本身可以用数值表示,比如:"123" "456"这样的字符串。

c、vm 字段:只有打开了 Redis 的虚拟内存功能,此字段才会真正的分配内存,该功能默认是关闭状态的。 Redis 使用 redisObject 来表示所有的 key/value 数据是比较浪费内存的,当然这些内存管理成本的付出主要也是为了给 Redis 不同数据类型提供一个统一的管理接口,实际作者也提供了多种方法帮助我们尽量节省内存使用

2、过期删除

过期数据的清除从来不容易,为每一条key设置一个timer,到点立刻删除的消耗太大,每秒遍历所有数据消耗也大,Redis使用了一种相对务实的做法: 当client主动访问key会先对key进行超时判断,过时的key会立刻删除。 如果clien永远都不再get那条key呢? 它会在Master的后台,每秒10次的执行如下操作: 随机选取100个key校验是否过期,如果有25个以上的key过期了,立刻额外随机选取下100个key(不计算在10次之内)。可见,如果过期的key不多,它最多每秒回收200条左右,如果有超过25%的key过期了,它就会做得更多,但只要key不被主动get,它占用的内存什么时候最终被清理掉只有天知道 .

3、String

a、可以是String,也可是是任意的byte[]类型的数组,如图片等。

b、对String代表的数字进行增减操作

c、BitMap

4、Hash

Redis 的 Hash 实际是内部存储的 Value 为一个 HashMap,并提供了直接存取这个 Map 成员的接口。Hash将对象的各个属性存入Map里,可以只读取/更新对象的某些属性。另外不同的模块可以只更新自己关心的属性而不会互相并发覆盖冲突。

Redis Hash 对应 Value 内部实际就是一个 HashMap,实际这里会有2种不同实现,** 这个 Hash 的成员比较少时 Redis 为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的 HashMap 结构,对应的 value redisObject 的 encoding 为 zipmap,当成员数量增大时会自动转成真正的 HashMap,此时 encoding 为 ht

5、set

set 的内部实现是一个 value 永远为 null 的 HashMap,实际就是通过计算 hash 的方式来快速排重的,这也是 set 能提供判断一个成员是否在集合内的原因。

6、sortedset

Sorted Set的实现是HashMap(element->score, 用于实现ZScore及判断element是否在集合内),和SkipList(score->element,按score排序)的混合体。SkipList有点像平衡二叉树那样,不同范围的score被分成一层一层,每层是一个按score排序的链表。

7、list

 

 

### Redis底层数据结构详解 Redis 是一种基于内存的键值存储系统,支持多种数据类型并针对不同场景进行了高效的编码优化。以下是 Redis 主要数据类型底层实现以及 `redisObject` 结构的作用。 #### 一、`redisObject` 结构解析 Redis 使用统一的对象表示来管理所有的键值对中的值。这种对象通过 `redisObject` 数据结构定义[^2]: ```c typedef struct redisObject { // 类型字段,占用4位,用于标记该对象属于哪种数据类型(字符串、列表等) unsigned type:4; // 编码字段,占用4位,指示当前对象的具体编码方式(int, embstr, raw 等) unsigned encoding:4; // LRU 字段,记录对象最近一次被访问的时间戳,用于淘汰策略计算 unsigned lru:REDIS_LRU_BITS; // 引用计数器,跟踪有多少地方正在使用这个对象 int refcount; // 指向实际底层数据结构的指针 void *ptr; } robj; ``` - **type**: 表明对象所属的数据类型,例如字符串 (`REDIS_STRING`) 或哈希表 (`REDIS_HASH`)。 - **encoding**: 描述具体使用的内部编码形式,例如整数值可能采用更紧凑的方式存储。 - **lru 和 refcount**: 提供了时间管理和资源分配的功能。 - **ptr**: 实际指向的是具体的底层数据结构实例。 #### 二、主要数据类型底层实现 ##### 1. 字符串 (String) 对于字符串类型Redis 支持三种不同的编码方式以适应性能需求: - **RAW**: 原始字符串保存在一个 C 风格字符数组中。 - **EMBSTR**: 小尺寸字符串会嵌入到 `robj.ptr` 自身之中,减少内存碎片化。 - **INT**: 如果字符串能够转换成整数,则直接存为机器原生整型变量。 这些设计使得短小简单的字符串能获得更高的缓存命中率和更低的空间开销[^3]。 ##### 2. 列表 (List) 列表有两种常见表现形式: - 当元素数量较少且长度较小时,使用压缩列表(ziplist)作为容器; - 对于较大的集合或者复杂情况则切换至双向链表(linkedlist),以便维持 O(1) 时间复杂度的操作特性。 ##### 3. 哈希表 (Hash) 类似于列表,在小型情况下优先考虑 ziplist;而当规模增大时转用字典(hash table)维护 key-value 映射关系。这样既能满足轻量级应用又能扩展到大规模环境下的高效查询能力。 ##### 4. 集合(Set)/有序集(ZSet) - Sets 只允许唯一成员存在,并通常由哈希表构成基础支撑。 - ZSets 不仅要求无重复项还附加分数排序功能,因此它实际上是由两个部分组成——一个普通的 set 加上另一个跳跃表(skip list) 来保持顺序排列。 以上就是关于 Redis 底层如何根据不同业务特点灵活调整其内部表达机制的一些介绍[^1]。 ```python # Python 示例展示如何操作 Redis 中的不同数据类型 import redis r = redis.Redis(host='localhost', port=6379) # String 操作 r.set('key_str', 'value') print(r.get('key_str')) # List 操作 r.lpush('my_list', 'item1') r.rpush('my_list', 'item2') print(r.lrange('my_list', 0, -1)) # Hash 操作 r.hset('hash_key', mapping={'field': 'val'}) print(r.hgetall('hash_key')) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值