数据结构:
-
简单动态字符串SDS
- 采用struct存储,有char []buf,len,free,O(1)的时间获取长度,空间分配自动扩容,小于1M倍增,多于1M,每次加1M。惰性删除,当字符串变短,不会缩容。
- 二进制安全:用char []buf存储,不会过滤等处理任何存储的数据
-
双向链表:无环、带表头和表尾指针,链表长度计数器。多态节点void *指针,可以通过dup,free,match三个属性设置类型特定函数。
-
字典:
- redis的字典采用hash表实现,每个字典有两个hash表,一个平时使用,一个rehash使用。
hash冲突采用拉链法解决,被分配到同一个索引上的多个键值会连接成为单向链表。 - 在对hash表扩容或收缩时,采用渐进式rehash完成,当发生curd时,每次增加旧hash表的索引id+1,处理这个hash到新hash上。
- redis的字典采用hash表实现,每个字典有两个hash表,一个平时使用,一个rehash使用。
-
跳跃表 skiplist:层 前进指针 跨度 后退指针 分值和成员
- 层:包含前进指针【指向当前层的下一个节点】和跨度【span记录两个节点之间的距离】。为1到32之间的一个随机数。
- 后退指针:用于从表尾向表头方向访问节点,每个节点只有一个后退指针,每一只能后退一个节点。
分值:节点按分值从小到大排序。当分值相同时,按照对象大小排序。
节点对象是一个指针,指向一个字符串对象SDS - 跳跃表是有序集合的底层实现之一。
-
整数集合inset是集合键的底层实现之一。
- 当一个集合只包含整数值元素时,并且这个集合的元素数量不多时,redis就会使用整数集合作为集合键的底层实现。包含三种编码int16_t、int32_t或者int64_t。
- 例如当前集合元素为1、2、3,此事编码为为int16_t,当添加65536(int32_t)时,需要升级为int32_t。在原有的基础上48位扩展到128位,65536添加到最后的96-127。先移动3从原来的32-47位移动到64-95位,再移动2,最后移动1 。
- 整数集合升级策略有两个好处,一是提升整数集合的灵活性,另一个是尽可能的节约内存。
- 整数集合不能降级,底层实现为数组。
-
压缩列表ziplist是列表键和哈希键的底层实现之一。
- 压缩列表视为了节约内存而开发的顺序型数据结构。可以包含多个节点,每个节点保存一个字节数组或者整数值。
- 每个加点包含前一个节点的长度,当前节点的编码和长度,节点内容。
- 添加或删除节点到压缩列表,可能发生连锁更新。
- 压缩列表结构:
zlbytes(压缩链表的长度4字节) zltail(压缩列表表尾节点距离起始节点地址的长度4字节) zllen(节点数量,2字节,当大于65535,长度需编列才能得到) entryX(数据节点多个) zlend(特殊值OXFF,标记压缩列表末端)。
对象类型
-
String字符串对象:
使用int,embstr(小于或等于39,一次分配redisObject和SDS)和raw(简单动态字符串,大于39字节,分两次分配redisObject和SDS)
。- 浮点数先转化为字符串,再存为emstr或raw。
- 字符串对象是Redis五种类型的对象中唯一一种会被其他四种对象嵌套的对象。
-
List列表对象:
ziplist压缩列表 或者 linkedlist双端列表。
- 编码转换:当列表对象保存的所有字符串元素的长度都小于64字节,列表对象保存的元素数量小于512个。不能满足这两个条件的列表对象需要使用linkedlist编码。元素小于64字节并长度小于512,采用ziplist。限制值的上限可以修改
-
Hash哈希对象:
ziplist或者hashtable
- ziplist保存:每一次添加键值对,都会把键压入ziplist末尾,在把值压入ziplist,一对键值总是紧挨在一起。
- 编码转换:哈希对象所有键值对的字符串长度小于64字节,并且哈希对象的键值对数量小于512,采用ziplist否则采用hashtable。限制值的上限可以修改。
-
Set集合对象的编码:
intset(整数集合)或者hashtable
- hashtable存储集合对象,使用字典作为底层实现,字典的每一个键都是一个字符串对象,每个字符串对象包含一个集合元素,而字典的值则全部被设置为NULL。
- 编码转换:集合对象保存所有元素都是整数值并且保存的元素数量不超过512个,采用intset编码,否则采用hashtable编码。
-
zset有序对象集合:
ziplist或者skiplist
- ziplist编码的有序集合对象使用压缩列表作为底层实现,每个集合使用紧挨在一起的压缩列表节点来保存,第一节点为元素的成员,第二个节点保存分值。按分值小道大排序。
- skiplist编码的有序对象(跳表和字典):在ziplist中按分值从小到大保存了所有的集合元素。通过这个跳跃表,程序可以对有序结合进行范围性操作。初次之外,还用dict字典为有序结合创建一个从成员到分值的映射,通过这个映射,ZSCORE可以在O(1)时间复杂度内查找成员的分值。
- 编码转换:元素小于128个并且元素成员长度都小于64字节,采用ziplist,否则采用跳表。
过期策略
- redis过期策略:惰性删除和定期删除相结合。
- 惰性删除,读写数据之前都会检查此字段,过期则删除。
- 定期删除:redis定时分多次遍历服务器中的各个数据库,随机检查一部分键是否过期,并删除其中的过期键。
- 我会在低用户访问时采用scan扫描redis,删除过期键,释放空间。
持久化:
- RDB:SAVE命令由服务器进程执行,会阻塞服务器。BGSAVE由子进程执行保存操作,不会阻塞服务器。
- AOF:保存所有修改数据库的写命令请求来记录数据库状态。命令请求会保存在AOF缓冲区里面,之后再定期写入并同步到AOF文件。