【笔记】Redis数据结构与对象《Redis设计与实现》
目录 ▼
😀 Redis(Remote Dictionary Server)是一种开源的内存数据结构存储系统,它提供了多种灵活且高效的数据结构,使得开发人员能够在应用程序中快速存储和检索数据。Redis的数据结构不仅仅是简单的键值对,它还支持更复杂的数据结构,如字符串、哈希表、列表、集合和有序集合等。这些数据结构不仅可以满足常见的数据存储需求,还可以支持更高级的操作,如排序、计数、范围查询等。
一、顶层数据结构
1.1 常用的基本结构
String
Redis 字符串存储字节序列,包括文本、序列化对象和二进制数组。 因此,字符串是最基本的 Redis 数据类型。 它们通常用于缓存,但它们支持额外的功能,让您也可以实现计数器并执行按位操作。
💡 By default, a single Redis string can be a maximum of 512 MB.
常用命令:
获取或者设置字符串
-
SET
-
SETNX
-
GET
-
MGET
计数器
-
INCRBY
-
INCR
-
DECR
-
DECRBY
List
用于实现队列或者栈
💡 The max length of a Redis list is 2^32 - 1 (4,294,967,295) elements.
常用命令
-
LPUSH
-
LPOP
-
LLEN
-
LMOVE
-
LTRIM
Set
无序无重复集合。
场景:去重、集合操作,抽奖
💡 The max size of a Redis set is 2^32 - 1 (4,294,967,295) members.
常用命令
-
SADD
-
SREM
-
SISMEMBER 测试一个String是否是set成员
-
SINTER 返回两个set的交集
-
SCARD 返回set数量
SMEMBER 在set数量比较大的情况下慎用,考虑使用SSCAN用于替换
Hash
用于记录键值对。也可以用于基础对象以及它的计数器,或者其他。
💡 Every hash can store up to 4,294,967,295 (2^32 - 1) field-value pairs. In practice, your hashes are limited only by the overall memory on the VMs hosting your Redis deployment.
常用命令
-
HSET
-
HGET
-
HMGET
-
HINCREBY
HKEYS、HVALS、HGETALL O(N) 复杂度
Sorted set
有序Set,带有一个权重的值。
场景:排行榜、限速器
常用命令
-
ZADD
-
ZRANGE
-
ZRANK 获取zset 中某个键的排行,正序(低到高)
-
ZREVRANK 获取zset 中某个键的排行,倒序 (高到低)
1.2 额外的基本结构
HyperLogLog
Bitmap
Geospatial indexe
二、底层数据结构
SDS、双端链表、字典、压缩列表、整数集合
SDS与C字符串的区别
-
常数复杂度获取字符串长度
-
杜绝缓冲区溢出
-
减少修改字符串时带来的内存重新分配次数
-
二进制安全
-
兼容部分C字符串函数

Redis只会使用C字符串作为字面量,在大多数情况下,Redis使用SDS(Simple Dynamic String,简单动态字符串)作为字符串表示。
链表
typedef struct listNode {
struct listNode *prev;
struct listNode *next;
void *value;
} listNode;
typedef struct listIter {
listNode *next;
int direction;
} listIter;
typedef struct list {
listNode *head;
listNode *tail;
void *(*dup)(void *ptr);
void (*free)(void *ptr);
int (*match)(void *ptr, void *key);
unsigned long len;
} list;
Redis的链表实现的特性
-
双端:
-
无环:
-
带表头指针和表尾指针:获取链表头尾的时间复杂度 O(1)
-
带链表长度计数器:使用 len 属性获取,O(1) 时间复杂度
-
多态:链表节点使用 void* 指针保存节点值,可以保存各种不同类型的值

字典
hashtable
/* This is our hash table structure. Every dictionary has two of this as we
* implement incremental rehashing, for the old to the new table. */
typedef struct dictht {
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
} dictht;
typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
// 指向下一个哈希表节点,形成链表(用于相同hash值的,解决冲突)
struct dictEntry *next;
} dictEntry;
字典
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
unsigned long iterators; /* number of iterators currently running */
} dict;
ht属性数一个包含两个项的数组,数组中的每个项都是一个 dictht 哈希表,一般情况下,字典只使用 ht[0] 哈希表, ht[1] 哈希表只会在对 ht[0] 哈希表进行 rehash 时使用。
如果没有在进行 rehash 那么它的值为 -1
哈希算法
Redis 使用 MurmurHash2 算法来计算哈希值。随机性分布,且计算速度快。
解决键冲突
链地址法,同一个哈希值的,每个新节点都放置在头指针位置。(因为没有尾指针,考虑到性能
)

服务一般是分多次进行 rehash,也就是渐进性hash。这里用到了 rehashidx 索引,也就是时候一个一个索引进行 迁移。
