概念

Redis作为一个开源的用C编写的非关系型数据库,基于优秀的CRUD效率,常用于软件系统的缓存,其本身提供了以下五种数据格式:

  • string:字符串
  • list:列表
  • hash:散列表
  • set:无序集合
  • zset:有序集合

接下来我们就要针对这五种数据结构,来分析其底层的结构
这里选用的版本是redis-5.0.4,所以可能有很多地方和如今网络上的其他博文不太一致,不同的地方我会在文中指出
string
因为redis使用c语言开发,所以自然没有java和c++的那些字符串类库,在redis中,其自己定义了一种字符串格式,叫做SDS(Simple Dynamic String),即简单动态字符串
这个结构定义在sds.h中:

typedef char *sds;

但是这个sds类型仅作为参数和返回值使用,并不是真正用于操作的类型,真正核心的部分是下面的这些类:

struct __attribute__ ((__packed__)) sdshdr5 {unsigned char flags; 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[];
};

除掉第一个结构体(已经弃用),sds具体类型的结构可以分为以下部分:

  • len:已使用的长度,即字符串的真实长度
  • alloc:除去标头和终止符(’\0’)后的长度
  • flags:低3位表示字符串类型,其余5位未使用(我暂时没发现redis在哪里使用过这个属性)
  • buf[]:存储字符数据

这里和老版本做一下对比,因为我手头只有4.x和5.x的版本,它们sds的实现是一致的,但是据其他人说sds之前的版本实现方式不同,有时间我会去下载下来看一下,其将字符串分为以下部分:

  • len:buf中已经占有的长度(表示此字符串的实际长度)
  • free:buf中未使用的缓冲区长度
  • buf[]:实际保存字符串数据的地方

redis同时写重写了大量的与sds类型相关的方法,那redis为什么要这么下功夫呢,有以下4个优点:

  • 降低获取字符串长度的时间复杂度到O(1)
  • 减少了修改字符串时的内存重分配次数
  • 兼容c字符串的同时,提高了一些字符串工具方法的效率
  • 二进制安全(数据写入的格式和读取的格式一致)

list
我们查看源文件可以看到有两个list,一个是ziplist,字面意是压缩列表,另一个是quicklist,字面意是快速列表,在redis中直接使用的是quicklist,但是我们先来看ziplist
ziplist
ziplist并不是一个类名,其结构是下面这样的: …
其中各部分代表的含义如下:

  • zlbytes:4个字节(32bits),表示ziplist占用的总字节数
  • zltail:4个字节(32bits),表示ziplist中最后一个节点在ziplist中的偏移字节数
  • entries:2个字节(16bits),表示ziplist中的元素数 entry:长度不定,表示ziplist中的数据
  • zlend:1个字节(8bits),表示结束标记,这个值固定为ff(255)

这些数据均为小端存储,所以可能有些人查看数据的二进制流与其含义对应不上,其实是因为读数据的方式错了
ziplist内部采取数据压缩的方式进行存储,压缩方式就不是重点了,我们仅从宏观来看,ziplist类似一个封装的数组,通过zltail可以方便地进行追加和删除尾部数据、使用entries可以方便地计算长度
但是其依然有数组的缺点,就是当插入和删除数据时会频繁地引起数据移动,所以就引出了quicklist数据类型
quicklist
其核心数据结构如下:

typedef struct quicklist {quicklistNode *head;quicklistNode *tail;unsigned long count;        /* ziplist所有节点的个数 */unsigned long len;          /* quicklistNode节点的个数 */int fill : 16;              /* 单个节点的填充因子 */unsigned int compress : 16; /* 压缩端结点的深度 */
} quicklist;

我们可以明显地看出,quicklist是一个双向链表的结构,但是内部又涉及了ziplist,我们可以这么说,在宏观上,quicklist是一个双向链表,在微观上,每一个quicklist的节点都是一个ziplist
在redis.conf中,可以使用下面两个参数来进行优化:

  • list-max-ziplist-size:表示每个quicklistNode的字节大小。默认为2,表示8KB
  • list-compress-depth:表示quicklistNode节点是否要压缩。默认为0,表示不压缩

这种存储方式的优点和链表的优点一致,就是插入和删除的效率很高,而链表查询的效率又由ziplist来进行弥补,所以quicklist就成为了list数据结构的首选
hash
hash这种结构在redis的使用时最为常见,在redis中,hash这种结构有两种表示:zipmap和dict
zipmap
zipmap其格式形如下面这样: <zmlen><len>"foo"<len><free>"bar"<len>"hello"<len><free>"world"
各部分的含义如下:

  • zmlen:1个字节,表示zipmap的总字节数
  • len:1~5个字节,表示接下来存储的字符串长度
  • free:1个字节,是一个无符号的8位数,表示字符串后面的空闲未使用字节数,由于修改与键对应的值而产生

这其中相邻的两个字符串就分别是键和值,比如在上面的例子中,就表示"foo" => "bar", "hello" => "world"这样的对应关系

这种方式的缺点也很明显,就是查找的时间复杂度为O(n),所以只能当作一个轻量级的hashmap来使用
dict
这种方式就适于存储大规模的数据,其格式如下:

typedef struct dict {dictType *type;/* 指向自定义类型的指针,可以存储各类型数据 */void *privdata; /* 私有数据的指针 */dictht ht[2];/* 两个hash表,一般只有h[0]有效,h1[1]只在rehash的时候才有值 */long rehashidx; /* -1:没有在rehash的过程中,大于等于0:表示执行rehash到第几步 */unsigned long iterators; /* 正在遍历的迭代器个数 */
} dict;

如果我们不想更深入的话了解到这种程度就可以了,其中真正存储数据的是dictEntry结构,如下:

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

很明显是一个链表,我们知道这是采用链式结构存储就足够了
这种方式会消耗较多的内存,所以一般数据较少时会采用轻量级的zipmap
set
在redis中,我们可以查看intset.h文件,这是一个存储整数的集合,其结构如下:

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

其中各字段含义如下:

  • encoding:数据编码格式,表示每个数据元素用几个字节存储(可取的值有2、4,和8)
  • length:元素个数
  • contents:柔性数组,这部分内存单独分配,不包含在intset中

具体的操作我们就不详细展开了,了解集合这种数据结构的应该都很清楚,我们这里说一下,intset有一个数据升级的概念,比方说我们有一个16位整数的set,这时候插入了一个32位整数,所以就导致整个集合都升级为32位整数,但是反过来却不行,这也就是柔性数组的由来
如果集合过大,会采用dict的方式来进行存储
zset
zset,有很多地方也叫做sorted set,是一个键值对的结构,其键被称为member,也就是集合元素(zset依然是set,所以member不能相同),其对应的值被称为score,是一个浮点数,可以理解为优先级,用于排列zset的顺序
其也有两种存储方式,一种是ziplist/zipmap的格式,这种方式我们就不过多介绍了,只需要了解这种格式将数据按照score的顺序排列即可
另一种存储格式是采用了skiplist,意为跳跃表,可以看成平衡树映射的数组,其查找的时间复杂度和平衡树基本没有差别,但是实现更为简单,形如下面这样的结构(图来源跳跃表的原理):

浅谈Redis五种数据结构的底层原理相关推荐

  1. 广义表头尾链表存储结构_详解Redis五种数据结构的底层原理

    1,redis有五种基本数据结构:string.hash.set.zset.list:底层redis是通过c语言来实现这w五种结构的,具体是如何实现的,我们具体看一下. 2,SDS "sim ...

  2. Redis五种数据结构及实现原理

    目录 1.Redis特性 2.Redis数据结构 2.1 字符串 2.1.1 字符串的基本操作 2.2 列表(list) 2.2.1 右边进左边出:队列 2.2.2 右边进右边出:栈 2.2.3 慢操 ...

  3. 万字长文的Redis五种数据结构详解(理论+实战),建议收藏。

    本文脑图 前言 Redis是基于c语言编写的开源非关系型内存数据库,可以用作数据库.缓存.消息中间件,这么优秀的东西一定要一点一点的吃透它. 关于Redis的文章之前也写过三篇,阅读量和读者的反映都还 ...

  4. Redis五种数据结构详解

    Redis是基于c语言编写的开源非关系型内存数据库,可以用作数据库.缓存.消息中间件,这么优秀的东西一定要一点一点的吃透它. Redis的五种数据结构包括以下五种: String:字符串类型 List ...

  5. 字符动图_图解redis五种数据结构底层实现(动图哦)

    redis有五种基本数据结构:字符串.hash.set.zset.list.但是你知道构成这五种结构的底层数据结构是怎样的吗?今天我们来花费五分钟的时间了解一下.(目前redis版本为3.0.6) 动 ...

  6. 图解redis五种数据结构底层实现

    redis有五种基本数据结构:字符串.hash.set.zset.list.但是你知道构成这五种结构的底层数据结构是怎样的吗?今天我们来花费五分钟的时间了解一下.(目前redis版本为3.0.6) 动 ...

  7. Redis五种基本数据类型底层详解(原理篇)

    Redis五种基本数据类型底层详解 详细介绍Redis用到的数据结构 简单动态字符串 SDS和C字符串的区别 总结 链表 字典 哈希表 字典 哈希算法 解决键冲突 rehash(重点) 渐进式reha ...

  8. 硬核资源!Redis 五种数据结构以及三种高级数据结构解析(详解)

    上一篇分享的是<深入理解JVM>,这篇给大家分享<Redis 五种数据结构以及三种高级数据结构解析>. 前言 在 Redis 最重要最基础就属 它丰富的数据结构了,Redis ...

  9. Redis五种数据结构及其使用场景

    Redis五种数据结构及其使用场景 先有个概念,redis 数据库其实就是一个大的 map,它容纳了所有的 key, key 都是 string 类型,而 value 则有 string, list, ...

最新文章

  1. 院士戴琼海:脑科学走向人工智能的重要路径
  2. 2011年7个最佳的Linux发行版
  3. C++在数字N中找到精确除以N的数字的算法(附完整源码)
  4. python绘制星空_用python画星空源代码是什么?
  5. 用 pycharm 可视化管理 sqlite 数据库
  6. 用户故事与敏捷方法笔记---搜集故事
  7. “软件工程造价师”和“软件造价评估师”有什么区别?
  8. CQF笔记M1L5仿真和操作随机微分方程
  9. 解决最新版chrome不能安装IDM插件IDMGCExt.crx的问题
  10. python封包和解包_Python基础——解包与封包
  11. 桃子CCD视觉高速喷射点胶机,用它你就会爱上它
  12. Java 匿名类热更新失败的原因
  13. STL——标准模板库
  14. MTK 9.0平台调试gsensor
  15. Clickhouse 时间日期函数实战总结
  16. Silverlight 4 - MVVM with Commanding and WCF RIA Services
  17. 安装 CentOS Ubuntu
  18. 图像处理:梯度与反色
  19. learn more study less:如何高效学习
  20. YDOOK:CSDN 1024 程序员节日专文:Ubuntu remotely install cuda

热门文章

  1. 重磅!!!微软发布.NET Core 2.2
  2. 老司机实战Windows Server Docker:3 单节点Windows Docker服务器简单运维(上)
  3. 微软发布正式版SQL Server 2016
  4. 微软CEO纳德拉开讲,2016微软开发者峰会在京召开
  5. PS2019进阶笔记(二)
  6. c语言时钟报告,C语言图形时钟课程设计实验报告
  7. 计算机控制系统的试题,计算机控制系统练习题(1)
  8. 我的未来计算机作文,我的未来作文(精选4篇)
  9. “Visual Studio 启动不能打开上次打开的文件” 最正确的解决姿势
  10. Andorid与webView交互,获取webView选中文字,兼容了iframe