当你对 redis 说你中意的女孩是 Mia
字数 1937 2025-08-11 08:36:09
Redis 内部存储机制详解
一、Redis 概述
Redis (Remote Dictionary Server) 是一个开源的、支持网络的、基于内存亦可持久化的 Key-Value 数据库,使用 ANSI C 语言编写,并提供多种语言的 API。
二、Redis 数据存储结构
2.1 键值对存储基础
当执行 SET myLove Mia 命令时,Redis 内部会进行以下处理:
- 将 key:myLove 和 value:Mia 包装成一个 dictEntry 对象
- 创建一个 redisObject 对象来存储值
2.2 dictEntry 结构
dictEntry 是 Redis 存储键值对的基本单元,结构如下:
typedef struct dictEntry {
// 键
void *key;
// 值
union {
void *val; // 指向具体redisObject
uint64_t u64;
int64_t s64;
} v;
// 指向下个哈希表节点,形成链表
struct dictEntry *next;
} dictEntry;
- Redis 内部使用一个大 hashmap 存储数据,通过数组实现 hash
- key 冲突通过链表解决
- 每个 dictEntry 存储一个 key-value 对,value 为 redisObject
2.3 Key 的存储
Key ("myLove") 不是直接以字符串存储,而是存储在 SDS (Simple Dynamic String) 结构中。
2.4 redisObject 结构
Value ("Mia") 存储在 redisObject 中,结构如下:
typedef struct redisObject {
// 类型 4bits
unsigned type:4;
// 编码方式 4bits
unsigned encoding:4;
// LRU 时间(相对于 server.lruclock) 24bits
unsigned lru:22;
// 引用计数 Redis里面的数据可以通过引用计数进行共享 32bits
int refcount;
// 指向对象的值 64-bit
void *ptr;
} robj;
ptr:指向具体的数据结构地址type:表示对象类型(String, List, Hash, Set, Zset 之一)encoding:表示底层使用的编码
2.5 Redis 底层数据结构
Redis 对象底层有八种数据结构:
REDIS_ENCODING_INT(long 类型的整数)REDIS_ENCODING_EMBSTR(编码的简单动态字符串)REDIS_ENCODING_RAW(简单动态字符串)REDIS_ENCODING_HT(字典)REDIS_ENCODING_LINKEDLIST(双端链表)REDIS_ENCODING_ZIPLIST(压缩列表)REDIS_ENCODING_INTSET(整数集合)REDIS_ENCODING_SKIPLIST(跳跃表和字典)
2.6 查看 redisObject 信息
# 查看 key 对应 value 的 redisObject 类型
type key
type myLove
# 查看 key 对应 value 的 redisObject 详细信息
debug object key
debug object myLove
不同 value 类型在 redisObject 中的表现:
- string 和 int 类型的 type 可能相同,但 encoding 不同
三、Redis 持久化机制
3.1 RDB 持久化
- 将数据以二进制形式写入磁盘文件
- 默认持久化方式
3.2 AOF 持久化
AOF (Append Only File) 持久化记录所有会更改 Redis 数据的命令:
-
配置 (redis.conf):
appendonly yes dir ./ appendfilename appendonly.aof -
同步过程:
- 命令传播:Redis 将执行完的命令、参数等信息发送到 AOF 程序
- 缓存追加:AOF 程序将命令转换为 RESP 协议格式,追加到 AOF 缓存
- 文件写入和保存:AOF 缓存内容写入文件,满足条件时调用 fsync 保存到磁盘
-
RESP 协议:
- Redis 客户端与服务器通信的序列化协议
- 通过特殊符号区分数据类型:
+:单行回复-:错误回复::整数回复$:批量回复*:多条批量回复
-
示例:
- 命令:
SET myLove "Mia" - 请求数据:
*3\r\n$3\r\nSET\r\n$6\r\nmyLove\r\n$3\r\nMia\r\n - 响应数据:
+OK\r\n
- 命令:
-
文件保存:
flushAppendOnlyFile函数执行:- WRITE:将 aof_buf 缓存写入 AOF 文件
- SAVE:调用 fsync 将文件保存到磁盘
四、键过期机制
4.1 设置键过期时间
EXPIRE myLove 999999999
4.2 过期字典
- RedisDb 结构中的 expires 字典保存所有键的过期时间
- 过期字典的 key 是指向键空间某个键对象的指针
- 过期字典的 value 是 long 类型整数,表示键的过期时间(毫秒级 UNIX 时间戳)
4.3 过期键判定
- 检查 key 是否存在于过期字典中
- 获取 key 的过期时间,判断当前 UNIX 时间戳是否大于过期时间
4.4 过期键删除策略
-
惰性删除:
- 由
expireIfNeeded函数实现 - 所有读写命令执行前都会调用此函数检查输入键
- 如果键已过期则删除,未过期则不处理
- 由
-
定期删除:
- 由
activeExpireCycle函数实现 - 服务器周期性操作时调用
- 在规定时间内分多次遍历数据库,随机检查部分键的过期时间并删除过期键
- 由
五、键删除操作
DEL myLove
注意:某些情况下键可能无法被删除(如被引用计数保护的数据)