Redis存储结构详解
Redis存储结构详解
Redis是一种高性能的键值存储系统,支持多种类型的数据结构,如字符串、列表、集合、散列等。本文将详细介绍Redis的存储结构,包括redisDb结构体、dict结构体和redisObject结构体。
一、整体结构图
二、redisDb结构体
Redis数据库由多个数据库组成,每个数据库用一个redisDb结构体来表示。以下是redisDb结构体的主要成员:
dict *dict;
指向一个字典结构的指针,字典内部存储Redis数据库中所有的键值对。键是字符串,值是Redis中的对象类型。字典内部使用哈希表实现,提供高效的查找性能,时间复杂度接近O(1)。
dict *expires;
存储键的过期时间。当键被设置过期时间后,这个字典会记录键和它的过期时间,使Redis能够自动删除过期的键。
dict *blocking_keys;
记录因某些操作(如列表的BLPOP或BRPOP命令)而阻塞的键和客户端。当有新数据可用时,这些客户端会被唤醒。
dict *ready_keys;
与blocking_keys相对应,记录已经准备好可以解除阻塞状态的键和客户端。当阻塞操作的条件满足时,客户端会从blocking_keys转移到ready_keys。
dict *watched_keys;
记录被WATCH命令监控的键和客户端。WATCH命令是事务的一部分,用于在事务执行前监控键,如果这些键在MULTI和EXEC之间被修改,事务将不会执行。
int id;
整数,唯一标识每个数据库。Redis默认有16个数据库,每个数据库都有一个从0到15的ID。
long long avg_ttl;
记录数据库中所有键的平均生存时间(Time To Live,TTL)。TTL是一个键在被自动删除前可以存活的时间。avg_ttl可以帮助了解数据的存活周期。
三、dict结构体
dictEntry是Redis中字典(dict)数据结构的一部分,用于存储键值对。dictEntry实例会被插入到一个哈希表中。哈希表由数组和链表组成,数组中的每个槽位可以指向一个dictEntry或链表的头结点。字典在Redis中扮演着重要角色,不仅用于存储数据库中的键值对,还用于存储过期时间、阻塞键、监视键等信息。dictEntry的内部结构如下:
typedef struct dictEntry {
void *key; // 键的指针
union {
void *val; // 值的指针
uint64_t u64; // 用于存储64位整数的值
double d; // 用于存储浮点数的值
} v;
struct dictEntry *next; // 指向哈希表中下一个条目的指针,用于解决哈希冲突
} dictEntry;
void *key:
指向键的指针。在Redis中,键通常是字符串,但这里使用void *类型是为了保持泛用性,允许键可以是任何类型的数据。
union { ... } v:
一个联合体,用于存储值。由于Redis支持多种数据类型,联合体提供了一种方式来存储不同类型的值:
void *val;:如果值是一个指针类型,比如指向一个更复杂的数据结构,那么可以使用这个字段。
uint64_t u64;:如果值是一个64位的整数,可以使用这个字段来直接存储整数值。
double d;:如果值是一个浮点数,可以使用这个字段来存储浮点数值。
unsigned long next:
指向链表中下一个dictEntry的指针。在Redis字典的哈希表中,如果两个键的哈希值相同,它们会被链接在同一链表中,next就用来维护这种链表结构。
unsigned long hash:
无符号长整型变量,存储键的哈希值。哈希值用于快速定位键在字典中的位置,从而加速键的查找过程。
unsigned long prev:
在较新版本的Redis中,dictEntry还包含了一个prev成员,它是指向链表中前一个dictEntry的指针。这使得字典能够更高效地遍历链表,以及在删除元素时更容易找到前一个元素。
四、redisObject结构体
redisObject是Redis中用于表示各种数据类型的基础结构体。在Redis内部,每当你存储一个键值对时,值部分实际上是一个redisObject的实例,它封装了数据本身以及关于数据的一些元信息。Redis支持多种数据类型,如字符串、列表、集合、有序集合、散列等。每种数据类型都可以通过redisObject进行抽象和统一管理。以下是redisObject的基本结构和相关说明:
typedef struct redisObject {
unsigned type:4; // 对象类型,4位用于存储类型信息
unsigned encoding:4; // 对象编码,4位用于存储编码信息
unsigned lru:LRU_BITS; // 访问时间戳,用于LRU淘汰算法
int refcount; // 对象的引用计数
void *ptr; // 指向实际数据的指针
} robj;
unsigned type:
表示对象的类型。在Redis中,数据可以是字符串(REDIS_STRING)、列表(REDIS_LIST)、集合(REDIS_SET)、有序集合(REDIS_ZSET)、哈希表(REDIS_HASH)、流(REDIS_STREAM)等类型之一。
unsigned encoding:
表示对象的编码方式,即数据如何在内存中存储。不同的编码方式可以节省内存或者提高读写速度。例如,字符串可以是普通的编码(REDIS_ENCODING_RAW),也可以是压缩后的编码(REDIS_ENCODING_EMBSTR或REDIS_ENCODING_LZF)。列表可以是双向链表(REDIS_ENCODING_LINKEDLIST),也可以是压缩列表(REDIS_ENCODING_ZIPLIST)。
unsigned lru:LRU_BITS:
用于存储对象最近访问时间戳的字段。Redis使用这个字段来实现LRU(最近最少使用)淘汰算法,从而在内存不足时淘汰一些不常用的键。
void *ptr:
指向实际数据的指针。对于字符串来说,这可能是一个指向字符串的指针;对于列表、集合等复合数据类型,这可能是指向数据结构(如链表、跳跃表)的指针。
int refcount:
引用计数,用于跟踪有多少地方引用了这个对象。在Redis中,对象可以被共享,引用计数机制帮助Redis管理内存,当引用计数降到零时,对象会被销毁。
int free:
这个成员在某些Redis版本中存在,用于在对象被销毁时预留一些空间,以便下次创建类似大小的对象时减少内存分配的开销。