Redis
简单来说就是,一个键值对类型的单线程内存数据库。
支持String、List、Set、Sorted Set、hashes数据类型。这些都在官网文档有说明
数据结构
简单动态字符串sds
简单动态字符串(Simple Dynamic String),redis是用C语言写的,但它的简单动态字符串却比C语言的字符串优秀。数据结构如图:
free:分配了但未使用的内存字节长度
len:已经使用的内存字节长度。不含 '\0'
C语言的字符串结构如图:
1. 获取字符串长度复杂的从从 O(N) 变成 O(1)
2. 在操作字符串时可以避免缓冲区溢出
3. 减少了修改字符串引起的内存重分配次数
4. 二进制安全:C语言字符串不能存某些特殊字符串,而简单动态字符串可以
内存策略:
空间预分配:修改时如果需要扩展,分配空间也分配未使用空间。free = min(len + 1MB)。总长度= len + 1byte + free.
惰性空间释放:缩短字符串时,不立即释放空间,而是用free记录数量
链表linkedlist
链表是一种常用的数据结构,不过C语言没有内置。实现也简单。
维护表头、表尾指针,表长度及操作表的函数。
字典与哈希表dict
字典由两个哈希表组成,其中一个正常情况下是空的,也增加了自己的属性。
内存策略:扩展与收缩,
渐进式rehash
1. 为 ht[1] 分配空间
2. 修改 rehashidx 为0,表示开始 rehash
3. 依次把ht[0] 里的所有键值对 rehash到 ht[1],每rehash 一个,rehashidx 加1
4. 期间 删除、查找、更新在两个哈希表上进行,添加元素则在新哈希表上进行。则最后旧表一定为空
5. 交互 ht[1] ht[0],同时 把 rehashidx 置为0
跳跃表skiplist
简单来说,跳跃表是链表的一种变形。在链表的基础上添加层level的概念。
待续
整数集合intset
从名字可知,用来存储整数的
contents 数组类型取决于encoding,而encoding取值有:INTSET_ENC_INT16、INTSET_ENC_INT32、INTSET_ENC_INT64,分别代表int16_t、int32_t、int64_t 类型的值。
升级:当新加元素的类型比现有所有元素的类型都长时,先升级整数集合类型,然后添加元素到数组开头或者末尾。
注意:因为新元素引起了升级,因此它肯定比所有元素大(为正数),放末尾;或者比所有元素小(为负数),放开头。
使用升级策略目的:提升整数集合的灵活性,再就是节约内存。
另外,整数集合不支持降级。
压缩列表ziplist
压缩列表是redis为了节约内存而开发的,由一系列特殊编码的连续内存块组成的顺序数据结构。包含任何多个节点 entry,每个节点保存一个字节数组或者一个整数值。
注意:跟其他数据结构不同,它不是结构体。严格来说它不是数据结构,只是有组织的数据存储方式而已。
连锁更新:previous_entry_lenght 记录了前一个节点长度,用1或则会5个字节。增加或者删除前一个节点时,当前节点记录的长度发生变化,因此需要扩展空间。依次需要把后边连续的内存空间移动。复杂度O(N).
redis对象
所谓redis对象,就是一个结构体对象,让 void *ptr 指向它所使用的上述介绍的各种结构存储。
用它包装之前说的数据结构构成redis支持的数据类型:
比如ptr指向简单动态字符串,构成字符串对象:
再比如哈希对象:
每种数据类型都有两种数据结构实现,字符串类型有三种。至于redis底层选择哪种,自行了解。这里简单列举下数据类型和结构组合
对象 | 对应type | 可用编码 |
|
字符串 | REDIS_STRING | OBJ_ENCODING_INT | 利用整数值实现的字符串 |
OBJ_ENCODING_EMBSTR | 利用embstr编码的sds实现的字符串对象 | ||
OBJ_ENCODING_RAW | 利用简单动态字符串实现的字符串 | ||
列表 | REDIS_LIST | OBJ_ENCODING_ZIPLIST | 使用压缩列表实现列表对象 |
OBJ_ENCODING_LINKEDLIST | 使用双端列表实现列表对象 | ||
哈希对象 | REDIS_HASH | OBJ_ENCODING_ZIPLIST | 使用压缩列表实现哈希对象 |
OBJ_ENCODING_HT | |||
集合 | REDIS_SET | OBJ_ENCODING_INTSET | 使用整数集合实现集合对象 |
OBJ_ENCODING_HT | 使用字典实现集合对象 | ||
有序集合 | REDIS_ZSET | OBJ_ENCODING_ZIPLIST | 使用压缩列表实现有序集合对象 |
OBJ_ENCODING_SKIPLIST | 使用跳跃表和字典实现有序集合对象 |
数据库
redis 服务器就是一个内存数据库。所有数据都保存在redisServer 结构中,其中又分键空间与过期空间两个字典:
服务器上可以有多个数据库,一般我们操作的是db[0],如果需要切换数据库,执行 SELECT x; x是数据库序号
对于每个数据库,都有键空间与过期空间。顾名思义
键空间存储键值对。
过期空间存储过期时间数据,但两个空间是共用键的。
客户端
所有客户端都存在list *clients 中,是一个列表。