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

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 深入Redis数据结构和底层原理

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)原理

网络异常,图片无法展示
|


(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)原理

网络异常,图片无法展示
|


(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)原理

网络异常,图片无法展示
|


(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)原理

网络异常,图片无法展示
|


(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)原理

网络异常,图片无法展示
|


(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 图解

网络异常,图片无法展示
|


3.2 查找过程

网络异常,图片无法展示
|


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
17天前
|
存储 消息中间件 NoSQL
Redis数据结构:List类型全面解析
Redis数据结构——List类型全面解析:存储多个有序的字符串,列表中每个字符串成为元素 Eelement,最多可以存储 2^32-1 个元素。可对列表两端插入(push)和弹出(pop)、获取指定范围的元素列表等,常见命令。 底层数据结构:3.2版本之前,底层采用**压缩链表ZipList**和**双向链表LinkedList**;3.2版本之后,底层数据结构为**快速链表QuickList** 列表是一种比较灵活的数据结构,可以充当栈、队列、阻塞队列,在实际开发中有很多应用场景。
|
21天前
|
存储 NoSQL Java
介绍下Redis 的基础数据结构
本文介绍了Redis的基础数据结构,包括动态字符串(SDS)、链表和字典。SDS是Redis自实现的动态字符串,避免了C语言字符串的不足;链表实现了双向链表,提供了高效的操作;字典则类似于Java的HashMap,采用数组加链表的方式存储数据,并支持渐进式rehash,确保高并发下的性能。
介绍下Redis 的基础数据结构
|
16天前
|
存储 NoSQL 关系型数据库
Redis的ZSet底层数据结构,ZSet类型全面解析
Redis的ZSet底层数据结构,ZSet类型全面解析;应用场景、底层结构、常用命令;压缩列表ZipList、跳表SkipList;B+树与跳表对比,MySQL为什么使用B+树;ZSet为什么用跳表,而不是B+树、红黑树、二叉树
|
16天前
|
存储 NoSQL Redis
Redis常见面试题:ZSet底层数据结构,SDS、压缩列表ZipList、跳表SkipList
String类型底层数据结构,List类型全面解析,ZSet底层数据结构;简单动态字符串SDS、压缩列表ZipList、哈希表、跳表SkipList、整数数组IntSet
|
12天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
86 9
|
3天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
12 1
|
6天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
9天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
11天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
38 4
|
1月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
28 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器

热门文章

最新文章