Redis数据类型
Redis一共有五种数据类类型。
String
String结构底层是一个简单动态字符串,支持扩容,存储字符串。
List
存储线性有序且可重复的元素,底层数据结构可以是双向链表/压缩列表
Set
存储不可重复的元素,一般用于求交集、差集等,底层数据结构可以是hash和整数数组。
Zset
存储的是有序不可重复的元素,Zset为每一个元素添加了一个Score属性作为排序依据,底层数据结构可以是ziplist和跳表
Hash
存储的是键值对,底层数据结构是ziplist和hash
Redis会在性能以及节省内存间考虑,选择最适合当前状态的底层数据结构实现。
Redis动态字符串(Simple Dynamic String——SDS)
Redis中保存的key是字符串,value往往是字符串或者字符串的集合。
Redis没有使用C语言的字符串, 因为
-
获取字符串长度需要计算
-
非二进制安全
-
不可修改
源码
struct __atrribute__ ((__packed__)) sdshdr8 {
uint8_t len; // buf已保存的字符串字节数
uint8_t alloc; // buf申请的总的字节数,不包含结束标志
unsigned char flags; // 不同SDS的头类型,用来控制SDS的头大小
char buf[];
}
动态扩容
假设现在有一个内容为“hi”的SDS,要给SDS追加一段字符串“,Amy”,这里首先会申请新内存空间:
-
如果新字符串小于1M, 则新空间为扩展后字符串长度的两倍+1(因为结束标识符
'\0',所以加1) -
如果新字符串大于1M,则新空间为扩展后字符串长度+1M+1, 称为内存预分配
优点
-
获取字符串长度的时间复杂度为O(1)
-
支持动态扩容
-
减少内存分配次数
-
二进制安全
IntSet
IntSet是Redis中set集合的一种实现方式,基于整数数组来实现,并且具备长度可变、有序等特征。
typedef struct intset {
uint32_t encoding; // 编码方式,支持存放16位,32位和64位整数
uint32_t length; // 元素个数
int8_t contents[]; // 整数数组, 保存集合数据
} intset;
encoding的三个模式
-
2字节整数,范围类似java的short
-
4字节整数,范围类似java的int
-
8字节整数,范围类似java的long
为了方便查找,Redis会将intset中的所有整数按照升序依次保存在contents数组中
inset的升级
假设有一个intset, 元素为{5, 10, 20}, 采用的编码是INSET_ENC_INT16,则每个整数占2字节。
向其中添加一个数字,50000。这个数字超过了int16_t的范围,intset会自动升级编码方式到合适的大小。
流程如下:
-
升级编码到INSET_ENC_INT32, 每个整数占4字节,并按照新的编码方式及元素个数扩容数组
-
倒序依次将数组中的元素拷贝到扩容后的正确位置(倒序的原因是防止数据被覆盖)
-
将待添加的元素放入数组末尾
-
最后,将intset的encoding属性改为INTSET_ENC_INT32,将length属性改为4