Redis运行于独立的进程,将数据保存在内存中,并提供多种手段持久化内存数据。
数据结构
Redis没有传统数据库的table模型。schema所对应的db仅以编号区分。实际使用中,通常以“:”作为分隔符,将命名空间值和业务key相连接。
Redis常用的value包含5种数据类型:string、list、set、map、sorted-sort
value通用数据结构
typedef struct redisObject{
unsigned type:4;
unsigned encoding:4;
unsigned lru:REDIS_LRU_BITS;
int refcount;
void *ptr;
} robj;
复制
type: 指的是string、list等结构化数据类型
encoding: 指的是结构化类型具体的实现方式,同一个类型可以有多种实现,例如String可以用int来承载,可以用封装的char[]来承载,List可以用ziplist或者链表来承载
lru: 本对象的空转时长,用于有限内存下长久不访问的对象的清理
refcount: 应用计数用于对象的垃圾回收
ptr: 以encoding方式实现这个对象的实际承载者的地址。
String
String可以表达3种值的类型:
字节串
整数
浮点数
三种类型根据具体场景由Redis完成相互间自动转型,整数和浮点数类型的value具备自增、自减等数字型操作。
针对String类型的value还具备简单的CAS操作,根据给定的key是否存在设置值。
基本操作
操作 | 描述 | 使用方法 |
---|---|---|
INCR | 将指定的key的内容加1 | INCR |
DECR | 将指定的key的内容减1 | DECR |
INCRBY | 将指定key的内容增加给定的值 | INCRBY |
DECRBY | 将指定key的内容减去给定的值 | DECRBY |
INCRBYFLOAT | 将指定key的内容增加给定的浮点数值 | INCRBYFLOAT |
APPEND | 将指定字节串的内容添加到指定key对应的value后 | APPEND |
GETRANGE | 对字节串value做范围截取 | GETRANGE |
SETRANGE | 将指定字符串内容覆盖,指定key对应的value,从指定位置开始覆盖 | SETRANGE |
STRLEN | 获取字节串的长度 | STRLEN |
GETBIT | 对字节串value,获取指定偏移量上的bit | GETBIT |
SETBIT | 将字节串value视为bit串并设置从给定起始位置起设置值 | SETBIT |
BITCOUNT | 将字节串value视为bit并统计1的数量 | BITCOUNT |
BITOP | 对多个key的value值做位操作,如:XOR、OR、AND、NOT | BITOP |
针对String类型的value还具备简单的CAS操作,根据给定的key是否存在设置值。
内存数据结构
String类型value内部以int、SDS(simple dynamic string)作为结构存储。int用来存放整形数据,sds存放字节/字符串和浮点型数据。
sds结构
typedef struct sdshdr{
unsigned int len;
unsigned int free;
char buf[];
};
复制
buf[]数组:存储了字节串的内容,但是它本身的长度通常大于所存储的内容的长度,所存储的内容的长度可以通过len字段直接在O(1)的复杂度内得出。
Redis采用类似C的做法存储字符串,即以'\0'结尾,但是这并不表示业务数据内容不能包含'\0'。
上图示例中,len的值为5,free的值为2。
buf中free区域的引入提升了sds对于字节串处理的能力,减少了处理过程中可能遇到的内存申请和释放的次数。如果APPEND操作的字节串的长度大于free的值,则需要扩容,如果小于free的值,不用进行扩容,不用重新申请内存。
对字节串长度进行缩减操作时,buf中的free部分不会立即释放而是继续保留以便后续操作利用。
buf的扩容和缩容
当业务操作超出了buf的现有容量时,sds会对buf进行扩容,触发条件如下:
字节串初始化时,buf的大小=len+1,即加上作为界定符的'\0'刚好等于业务数据的长度。
当对串的操作完成后预期的串长度小于1MB时,扩容后的buf大小=业务串预期长度*2+1,即不考虑最后的'\0',buf倍增
对于大于1MB的长串,buf总是留出1MB的free空间,即buf以业务串的2倍来扩容,单最大留出1MB的空间。
字节串和字符串
sds中存储的内容可以是ASCII字符串,也可以是字节串。由于sds通过len字段确定业务串的长度,业务串可以存储非文本内容。
sds编码优化
value部分通常由两部分构成:redisObject和redisObject的ptr指向的sds部分。在创建value对象时,通常需要为redisObject和sds申请两次内存。对于很多短小的字节串,sds长度较小,可以把redisObject和sds连续存放,在创建redisObject时,一次性将sds对象的内存也申请了,初始化redisObject时同时设置sds的内容。