【笔记】Redis数据结构与对象《Redis设计与实现》
学习笔记

【笔记】Redis数据结构与对象《Redis设计与实现》

· 约 3,045 字 · 阅读约 16 分钟
目录

😀 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字符串的区别

  1. 常数复杂度获取字符串长度

  2. 杜绝缓冲区溢出

  3. 减少修改字符串时带来的内存重新分配次数

  4. 二进制安全

  5. 兼容部分C字符串函数

image

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* 指针保存节点值,可以保存各种不同类型的值

image

字典

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 算法来计算哈希值。随机性分布,且计算速度快。

解决键冲突

链地址法,同一个哈希值的,每个新节点都放置在头指针位置。(因为没有尾指针,考虑到性能

image

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

image

相关文章