redis五种数据结构特点
redis使用五种数据结构,分别是string(字符串),list(列表),hash(哈希表),set(集合),zset(有序列表)。
下面介绍他们的一些特点
redis-string
介绍
string是redis中最基础的数据结构,就是KV键值对中V值的类型是字符串。
SDS内部存储数据结构
redis没有使用char数组来存储字符串,而是使用SDS(simple dynamic string)来存储字符串。SDS是一种数据结构,定义为:
c++
struct sdshdr {
//记录buf数组中已使用字节的数量
//等于sds所保存字符串的长度
int len;
//记录buf数组中未使用字节的数量
int free;
//字节数组,用于保存字符串
char buf[];
}
free属性表示这个SDS未使用的空间;
len属性表示这个SDS保存了一个len字节长的字符串;
buf属性是一个char类型的数组,它是一个柔性数组,前面的字节保存了字符串,最后一个字节则保存了空字符'\0'。(保留这种设计是为了让SDS可以直接重用一部分C字符串函数库里面的函数)
相比于c字符串,SDS的优势:
- SDS获取字符串长度的时间复杂度是O(1),c字符串是O(n);
- SDS在字符串长度增减时自动处理分配空间,杜绝内存溢出的操作;相比c字符串,减少了内存重分配的次数;
- SDS是二进制安全的。C字符串中的字符必须符合某种编码(比如ASCII),并且除了字符串的末尾之外,字符串里面不能包含空字符,否则最先被程序读入的空字符将被误认为是字符串结尾,这些限制使得C字符串只能保存文本数据,而不能保存像图片、音频、视频、压缩文件这样的二进制数据。而SDS不会出现因为特殊字符导致字符串被截断的问题,SDS的API都是二进制安全的(binary-safe),所有SDS API都会以处理二进制的方式来处理SDS存放在buf数组里的数据。
三种编码方式
redis为了更好的节省空间,采用不同方式的存储。
- 当保存的是 Long 类型整数时,RedisObject 中的指针就直接赋值为整数数据了,这样就不用额外的指针再指向整数了,节省了指针的空间开销。
- 当保存的是字符串数据,并且字符串小于等于 44 字节时,RedisObject 中的元数据、指针和 SDS 是一块连续的内存区域,这样就可以避免内存碎片。这种布局方式也被称为 embstr 编码方式。
- 当保存的是字符串数据,并且字符串大于 44 字节时,SDS 的数据量就开始变多了,Redis 就不再把 SDS 和 RedisObject 布局在一起了,而是会给 SDS 分配独立的空间,并用指针指向 SDS 结构。这种布局方式被称为 raw 编码模式。
特点总结
- 采用SDS数据结构进行存储,相比c字符串效率更高,更安全
- 当字符串是long类型整数时,直接采用int格式存储;
- 当字符串是<=44字节的字符串,采用embstr格式存储;
- 当字符串是>44字节的字符串,采用raw格式存储;
redis-list
介绍
list在redis中代表一种可以双向插入,删除的链表。
quicklist
quicklist是redis中list使用的数据结构,它是由双向链表和ziplist组成的。
其主体是一个双向链表,链表中的每一个元素是一个ziplist,每当ziplist超过list-max-ziplist-size
大小的时候(默认8字节),就会新建一个ziplist。
这样设计的好处是即保留了链表头尾插入,删除元素的高效,也使得中间元素的查询变得更加高效。
特点总结
- 采用quicklist进行存储
redis-hash
特点总结
- redis中的hash使用ziplist和hashtable作为存储数据结构
- 使用ziplist的条件:
-- key < 64 bytes
-- value < 64 bytes
-- 元素个数 < 521
-- 否则就会使用hashtable - redis中的hashtable是使用dictht实现的
其中,table 结构是一个 dictEntry 类型的数组,而 dictEntry 是一个单向链表中的节点,代表的是 hashtable 中的一条记录,包含了该条记录的 key、value 以及指向下一个节点(下一条记录)的指针(hash 碰撞)。
dictEntry 中的 key 和 val 都是 void 类型,意味着可以存储任意数据类型。所以,即使 Redis 中一些数据类型本身的底层实现也是 hashtable(set、zset、hash 等),但仍然可以存储在 redisDb 中的 dict 中。
dictht 中的 size 表示 hashtable 中 bucket 的数量,sizemask 是 size 的位掩码,用于计算对应的 key 应该存储在哪个 bucket 当中(位运算的效率高于取模运算)。而 used 则用来存储当前 hashtable 中实际存储的记录(元素)的数量。
redis-set
介绍
set在redis中代表无序集合,其中元素的key值不可以重复出现。
特点总结
- set存储结构是两种:intset和hashtable
- intset使用条件:
-- V值类型是数字
-- 元素个数 < 512
-- 否则使用hashtable来存储
redis-zset
介绍
zset在redis中代表有序集合,其中元素key值不可以重复出现,按照给定的score排序
特点总结
- zset使用ziplist和skiplist作为存储结构
- 使用ziplist的条件:
-- 元素个数 < 128
-- key < 64 bytes
-- value < 64bytes
-- 否则使用skiplist