redis基础数据结构详解

一. redis为什么快

  1. 基于内存的存储
  2. 虽然是单线程,但是采取了多路复用,可以高效的处理网络并发
  3. 良好的数据结构设计

二.redis基础数据结构

redis有五种基础的数据结构 string,list,set,zset,hash
redis所有的数据结构的key都是string类型,我们所说的数据结构都是指value的数据结构

1. 字符串

redis中的字符串并没有采用C语言的字符串,而是对其进行了重新设计,redis中的字符串叫做SDS(Simple Dynamic String)
SDS的数据结构如下

struct sdshdr{
    //记录buf数组中已使用字节的数量
    int len;
    
    //记录buf数组中未使用字节的数量
   	int free;
    
    //字节数组,用于保存字符串
    char buf [];
}

C字符串和SDS之间的区别:

  • C语言字符串返回一个字符串长度需要从头到尾遍历,时间复杂度为O(N),SDS因为有len属性,所以可以直接返回len即可
  • C语言字符串不能很好得防止缓冲区溢出,需要程序员手动判断一下,例如C字符串修改/拼接的时候,SDS字符串进行类似操作时,会先判断内存是否足够操作使用,如果不够使用,会先对SDS进行扩容
  • C语言字符串对字符串进行修改必须重新对内存进行分配,SDS修改字符串时候并不一定会对内存进行重新分配,主要是因为采取了以下策略:
    a. 空间预分配,当SDS扩容的时候,除了分配SDS所将要使用的内存空间外,还会分配和字符串内存相同的大小的内存空间
    b. 惰性空间释放,当SDS释放内存空间时,并不会立刻释放内存空间,只需要使用free记录下即可
  • SDS是安全的字符串,可以保存图片,音频等数据

SDS不同编码
int 当字符串对象值为整型,redis会直接用一个整数值来表示字符串对象
embstr
raw

以下为三种编码方式的内存布局
img

embstr:
一次预分配空间,只读,修改后自动转换为raw编码模式
增长超过44字节自动转换为raw编码模式
raw编码模式不可回退到embstr编码模式
只适合长点较小的字符串,小于44字节

raw编码模式
需要两次分配内存空间(分别为redisObject和sds分配内存空间)
保存大于44字节的字符串

embstr到raw的转换
在redis中,所有的存储都是以KV键值对的形式存储的,K是字符串类型,就是SDS;V 可能是字符串、list、hash等(Redis支持的数据结构),V并没有直接定成具体的类型,而是用redisObject封装了一层;实际存储的数据结构是由ptr指针具体指向。
并且,redis为了更好的节省空间,ptr指针也有不同方式的存储,一方面,当保存的是 Long 类型整数时,RedisObject 中的指针就直接赋值为整数数据了,这样就不用额外的指针再指向整数了,节省了指针的空间开销。另一方面,当保存的是字符串数据,并且字符串小于等于 44 字节时,RedisObject 中的元数据、指针和 SDS 是一块连续的内存区域,这样就可以避免内存碎片。这种布局方式也被称为 embstr 编码方式。当然,当字符串大于 44 字节时,SDS 的数据量就开始变多了,Redis 就不再把 SDS 和 RedisObject 布局在一起了,而是会给 SDS 分配独立的空间,并用指针指向 SDS 结构。这种布局方式被称为 raw 编码模式。

扩容:SDS在1M以下时,每次扩容会把当前容量扩大一倍,到了1M以后,每次扩容会增加1M容量

2. list

redis老版本采用ziplist和linkedlist表达list,但是由于linkedlist过于浪费内存,采用quicklist表达list,quicklist采用了ziplist和linkedlist的方式进行结合,quicklistnode结构包含ziplist,组成双向链表。ziplist默认超过8k就新开ziplist

quicklist的数据结构如下

struct ziplist {
	...
}
struct ziplist_compressed {
	int32 size;
	byte[] compressed_data;
}


struct quicklistNode {
	quicklistNode* prev;
	quicklistNode* next;
	ziplist* zl; // 指向压缩列表
	int32 size; // ziplist 的字节总数
	int16 count; // ziplist 中的元素数量
	int2 encoding; // 存储形式 2bit,原生字节数组还是 LZF 压缩存储
	...
}
struct quicklist {
	quicklistNode* head;
	quicklistNode* tail;
	long count; // 元素总数
	int nodes; // ziplist 节点的个数
	int compressDepth; // LZF 算法压缩深度
	...
}

3.hash

hash有两种数据结构

  • ziplist
  • dict

4.zset

zset底层两种数据结构

  • ziplist
  • hash+skiplist

5. set

hashtable(key为set里面的值,value为null)
inset(有序数组)

posted @ 2023-04-06 19:19  itqczzz  阅读(26)  评论(0编辑  收藏  举报