底层结构
redis没有表的概念,只有db,数据都放在db中。
RedisDB结构
typedef struct redisDb {
int id; //id是数据库序号,为0-15(默认Redis有16个数据库)
long avg_ttl; //存储的数据库对象的平均ttl(time to live),用于统计
dict *dict; //存储数据库所有的key-value
dict *expires; //存储key的过期时间
dict *blocking_keys;//blpop 存储阻塞key和客户端对象
dict *ready_keys;//阻塞后push 响应阻塞客户端 存储阻塞后push的key和客户端对象
dict *watched_keys;//存储watch监控的的key和客户端对象
} redisDb;
- id:数据编号,默认是0-15(16个数据库)
- avg_ttl:用于统计数据库中对象的平均存储时间
- dict: 存储所有的key-value
- expires:存储key的过期时间 (定时删除,惰性删除,主动删除,LRU)
RedisObject
具体指的就是value,value的一个对象。
结构:
typedef struct redisObject {
unsigned type:4;//类型 五种对象类型
unsigned encoding:4;//编码
void *ptr;//指向底层实现数据结构的指针
//...
int refcount;//引用计数
//...
unsigned lru:LRU_BITS; //LRU_BITS为24bit 记录最后一次被命令程序访问的时间 //...
}robj;
- type:对象的类型,一共5种基本数据类型,type key。如:String。占4位。
- encoding:编码方式,通过object encoding查看对象的编码方式。占4位
- ptr:指针,指向对象的值
- refcount:引用计数,用来记录对象被引用的次数,主要用于内存的回收(gc的引用计数)。整数。
- LRU:前16位用于记录key的最近(最后)被访问的时间。 后8位用于计数最近被访问的次数。占24位。
7种type
String字符串类型
本质上是SDS,对原来的C语言字符串做了封装。 结构:
struct sdshdr{
//记录buf数组中已使用字节的数量
int len;
//记录 buf 数组中未使用字节的数量
int free;
//字符数组,用于保存字符串
char buf[];
}
- len:已使用的字节数量
- free:数组剩余字节数
- buf[]:保存的字符串 好处:
- SDS在C字符串的基础上加了len和free字段,获取字符串长度的复杂度位O(1),原来为O(n)。
buf数组长度为free+len+1 (最后会有一个空字符"\0")。 - SDS记录了已使用和未使用的字符串长度,在可能造成缓冲区溢出时会自动分配内存,杜绝了缓冲区溢出。
- 可以存取二进制数据,以字符串长度len来作为结束标识。
C: \0 空字符串 二进制数据包括空字符串,所以没有办法存取二进制数据
SDS : 非二进制 \0
扩容机制
待补充。。。。
跳跃表
sorted-set有序集合的底层实现,效率高,实现简单。
原理:有序列表进行分层处理,每一层都是一个有序列表。
借鉴图:
插入与删除
上面例子中,9个结点,一共4层,是理想的跳跃表。 通过抛硬币(概率1/2)的方式来决定新插入结点跨越的层数: 正面:插入上层 背面:不插入 达到1/2概率(计算次数)
牺牲空间,提升时间。
源码实现C
//跳跃表节点
typedef struct zskiplistNode {
sds ele; /* 存储字符串类型数据 redis3.0版本中使用robj类型表示, 但是在redis4.0.1中直接使用sds类型表示 */
double score;//存储排序的分值
struct zskiplistNode *backward;//后退指针,指向当前节点最底层的前一个节点
/* 层,柔性数组,随机生成1-64的值 */
struct zskiplistLevel {
struct zskiplistNode *forward; //指向本层下一个节点
unsigned int span;//本层下个节点到本节点的元素个数
} level[];
} zskiplistNode;
//链表
typedef struct zskiplist{
//表头节点和表尾节点
structz skiplistNode *header, *tail;
//表中节点的数量 unsigned long length;
//表中层数最大的节点的层数
int level;
}zskiplist;
字典
散列表(hash)。。待补充
压缩列表
ziplist是由一系列特殊编码的连续内存块组成的顺序型数据结构。
压缩列表结构:
entryX元素编码结构:
- previous_entry_length:前一个元素的长度
- encoding:编码方式
- content:数据内容
快速列表
整数集合
一个有序的整数集合,存储整数的连续存储结构。
结构图:
流对象(不太懂)
stream主要由:消息、生产者、消费者和消息组成。
Redis Strean的底层主要使用了liststpack(紧凑列表)和Rax(基数树组成)。
10种encoding
set对象有:
- intset:元素是64位以内的整数
- hashtable:元素是64位以外的整数 String对象有
- int:int类型的整数
- raw:简单动态字符串,长度大于44字节
- embstr:简单动态字符串,长度小于44字节 list有
- quicklist 快速列表
在较早版本的 redis 中,list 有两种底层实现:
- 当列表对象中元素的长度比较小或者数量比较少的时候,采用压缩列表 ziplist 来存储
- 当列表对象中元素的长度比较大或者数量比较多的时候,则会转而使用双向列表 linkedlist 来存储
- 两者各有优缺点:
ziplist 的优点是内存紧凑,访问效率高,缺点是更新效率低,并且数据量较大时,可能导致大量的内存复制
linkedlist 的优点是节点修改的效率高,但是需要额外的内存开销,并且节点较多时,会产生大量的内存碎片
为了结合两者的优点,在 redis 3.2 之后,list 的底层实现变为快速列表 quicklist。
- 快速列表是 linkedlist 与 ziplist 的结合: quicklist 包含多个内存不连续的节点,但每个节点本身就是一个 ziplist。
hash 字典和压缩列表
- dict:当散列表元素个数比较多,或元素不是小整数和短字符串时。
- ziplis:当散列表元素比较少,且元素是小整数和短字符串时。 set 整型集合和字典
- intset:当元素都是整数,并且在64位整数范围内。
- dict:其他情况下。(hashtable) zset 压缩列表和跳跃表+字典
- skiplist + dict 当元素的个数比较多或元素不是小整数或短字符串时。
- ziplist 当散列表元素的个数比较少,且元素都是小整数或短字符串时。