深入Redis数据结构和底层原理

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis为什么能支持每秒钟十万级的高并发?其中一个重要的原因,就是Redis中高效的数据结构,因此我们就专门的来研究下Redis的核心数据结构,Go!

1 概述

Redis为什么能支持每秒钟十万级的高并发?

  • 基于内存的存取方式
  • 高效的数据结构
  • 单线程,使用多路I/O复用模型,非阻塞IO
  • ......

其中一个重要的原因,就是Redis中高效的数据结构,因此我们就专门的来研究下Redis的核心数据结构,Go!

2 五大基本数据结构

分别是String、List、Set、ZSet、Map

  • String类型:一个String类型的value最大可以存储512M
  • List类型:list的元素个数最多为2^32-1个,也就是4294967295个。
  • Set类型:元素个数最多为2^32-1个,也就是4294967295个。
  • Hash类型:键值对个数最多为2^32-1个,也就是4294967295个。
  • Sorted set类型:跟Sets类型相似,但是有序。

2.1 String

(1)使用

127.0.0.1:6379> set str hello
OK
127.0.0.1:6379> get str
"hello"

(2)原理
image.png

(3)源码

typedef char *sds;

/* Sdshdr5从未被使用过,我们只是直接访问标记字节,在这里是为了记录类型5 SDS字符串的布局。*/
struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 3LSB的类型,5MSB的字符串长度   */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; 
    uint8_t alloc; /* 排除头和空结束符 */
    unsigned char flags; 
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; 
    uint16_t alloc; 
    unsigned char flags; 
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; 
    uint32_t alloc; 
    unsigned char flags; 
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len;
    uint64_t alloc;
    unsigned char flags; 
    char buf[];
};

2.2 List

(1)使用

127.0.0.1:6379> lpush items a b c
(integer) 3
127.0.0.1:6379> lrange items 1 10
1) "b"
2) "a"
127.0.0.1:6379> lrange items 0 10
1) "c"
2) "b"
3) "a"
127.0.0.1:6379> lpush items d
(integer) 4
127.0.0.1:6379> lrange items 0 10
1) "d"
2) "c"
3) "b"
4) "a"
127.0.0.1:6379> lpop items
"d"
127.0.0.1:6379> lrange items 0 10
1) "c"
2) "b"
3) "a"

(2)原理
image.png

(3)源码

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;

2.3 Set

(1)使用

127.0.0.1:6379> sadd set_items a b c c d d
(integer) 4
127.0.0.1:6379> smembers set_items
1) "c"
2) "b"
3) "d"
4) "a"
127.0.0.1:6379> sadd set_items e
(integer) 1
127.0.0.1:6379> smembers set_items
1) "d"
2) "a"
3) "e"
4) "b"
5) "c"
127.0.0.1:6379>

(2)原理

image.png
(3)源码

typedef struct intset {
    uint32_t encoding;
    uint32_t length;
    int8_t contents[];
} intset;

2.4 ZSet

(1)使用

127.0.0.1:6379> zadd zset_items 1 a 2 c 3 b
(integer) 3
127.0.0.1:6379> zrange zset_items 0 10
1) "a"
2) "c"
3) "b"
127.0.0.1:6379> add 2 d
(error) ERR unknown command 'add'
127.0.0.1:6379> zadd zset_items 2 d
(integer) 1
127.0.0.1:6379> zrange zset_items 0 10
1) "a"
2) "c"
3) "d"
4) "b"

(2)原理

image.png

(3)源码

/* ZSET使用特殊版本的跳表 */
typedef struct zskiplistNode {
    sds ele;
    double score; 
    struct zskiplistNode *backward;
    struct zskiplistLevel { // 跳表的层数
        struct zskiplistNode *forward;
        unsigned long span;
    } level[];
} zskiplistNode;

typedef struct zskiplist {
    struct zskiplistNode *header, *tail; //头指针和尾指针
    unsigned long length;
    int level;
} zskiplist;

typedef struct zset {
    dict *dict; //哈希表
    zskiplist *zsl; //跳表
} zset;

2.5 Map

(1)使用

127.0.0.1:6379> hmset map name zs age 12
OK
127.0.0.1:6379> hgetall map
1) "name"
2) "zs"
3) "age"
4) "12"
127.0.0.1:6379> hget map name
"zs"
127.0.0.1:6379> hmset map school zz
OK
127.0.0.1:6379> hgetall map
1) "name"
2) "zs"
3) "age"
4) "12"
5) "school"
6) "zz"
127.0.0.1:6379> hdel map age
(integer) 1
127.0.0.1:6379> hgetall map
1) "name"
2) "zs"
3) "school"
4) "zz"
127.0.0.1:6379>

(2)原理

image.png
(3)源码

typedef struct dictEntry {
    void *key;
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next;
} dictEntry;

typedef struct dictType {
    uint64_t (*hashFunction)(const void *key);
    void *(*keyDup)(void *privdata, const void *key);
    void *(*valDup)(void *privdata, const void *obj);
    int (*keyCompare)(void *privdata, const void *key1, const void *key2);
    void (*keyDestructor)(void *privdata, void *key);
    void (*valDestructor)(void *privdata, void *obj);
} dictType;

/* 这是哈希表结构。 当我们实现增量重哈希时,每个字典都有两个这样的表,从旧表到新表。 */
typedef struct dictht {
    dictEntry **table;
    unsigned long size;
    unsigned long sizemask;
    unsigned long used;
} dictht;

typedef struct dict {
    dictType *type;
    void *privdata;
    dictht ht[2];
    long rehashidx; 
    unsigned long iterators; /* 当前运行的迭代器数量 */
} dict;

3 重点讲下跳表(SkipList)这个数据结构

3.1 图解

image.png

3.2 查找过程

image.png

相关文章
|
存储 缓存 NoSQL
Redis 服务器全方位介绍:从入门到核心原理
Redis是一款高性能内存键值数据库,支持字符串、哈希、列表等多种数据结构,广泛用于缓存、会话存储、排行榜及消息队列。其单线程事件循环架构保障高并发与低延迟,结合RDB和AOF持久化机制兼顾性能与数据安全。通过主从复制、哨兵及集群模式实现高可用与横向扩展,适用于现代应用的多样化场景。合理配置与优化可显著提升系统性能与稳定性。
368 0
|
3月前
|
消息中间件 缓存 NoSQL
Redis各类数据结构详细介绍及其在Go语言Gin框架下实践应用
这只是利用Go语言和Gin框架与Redis交互最基础部分展示;根据具体业务需求可能需要更复杂查询、事务处理或订阅发布功能实现更多高级特性应用场景。
303 86
|
3月前
|
存储 消息中间件 NoSQL
Redis数据结构:别小看这5把“瑞士军刀”,用好了性能飙升!
Redis提供5种基础数据结构及多种高级结构,如String、Hash、List、Set、ZSet,底层通过SDS、跳表等实现高效操作。灵活运用可解决缓存、计数、消息队列、排行榜等问题,结合Bitmap、HyperLogLog、GEO更可应对签到、UV统计、地理位置等场景,是高性能应用的核心利器。
|
3月前
|
存储 缓存 NoSQL
Redis基础命令与数据结构概览
Redis是一个功能强大的键值存储系统,提供了丰富的数据结构以及相应的操作命令来满足现代应用程序对于高速读写和灵活数据处理的需求。通过掌握这些基础命令,开发者能够高效地对Redis进行操作,实现数据存储和管理的高性能方案。
123 12
|
3月前
|
存储 消息中间件 NoSQL
【Redis】常用数据结构之List篇:从常用命令到典型使用场景
本文将系统探讨 Redis List 的核心特性、完整命令体系、底层存储实现以及典型实践场景,为读者构建从理论到应用的完整认知框架,助力开发者在实际业务中高效运用这一数据结构解决问题。
|
7月前
|
存储 NoSQL 算法
Redis设计与实现——数据结构与对象
Redis 是一个高性能的键值存储系统,其数据结构设计精妙且高效。主要包括以下几种核心数据结构:SDS、链表、字典、跳跃表、整数集合、压缩列表。此外,Redis 对象通过类型和编码方式动态转换,优化内存使用,并支持引用计数、共享对象和淘汰策略(如 LRU/LFU)。这些特性共同确保 Redis 在性能与灵活性之间的平衡。
|
存储 消息中间件 NoSQL
Redis 数据结构与对象
【10月更文挑战第15天】在实际应用中,需要根据具体的业务需求和数据特点来选择合适的数据结构,并合理地设计数据模型,以充分发挥 Redis 的优势。
249 64
|
存储 NoSQL 算法
「Redis」数据结构与对象
Redis数据结构与对象介绍
160 0
|
NoSQL 算法 Java
Redis进阶 - 数据结构:对象机制详解,一文深入底层分析
我们在前文已经阐述了Redis 5种基础数据类型详解,分别是字符串(string)、列表(list)、哈希(hash)、集合(set)、有序集合(zset),以及5.0版本中Redis Stream结构详解;那么这些基础类型的底层是如何实现的呢?Redis的每种对象其实都由对象结构(redisObject) 与 对应编码的数据结构组合而成, 本文主要介绍对象结构(redisObject) 部分。
Redis进阶 - 数据结构:对象机制详解,一文深入底层分析

热门文章

最新文章