1 数据类型
这些数据结构我们并不会用到,他是redis底层的数据结构.
我们能够用到的是对象
1.1 简单动态字符串
redis中的字符串使用的是
struct sdshdr { int len; //已用长度 int free; //未用长度 char buf[]; // buf 的大小为len+free+1 }
最后依然兼容c的字符串,最后一个字节使用‘\0‘
,因此最终的总长度为len+free+1
相比较c的字符串优点在于:
- o(1)内获得字符串长度
- 避免了缓冲区溢出
- 减少了修改字符串带来的内存充分配次数
- 二进制安全,使用len记录长度,而不是
\0
因此字符串中可以出现
‘\0‘
空间与分配
小于1M时,分配:需要长度×2+1?,因此最终len和free相同
大于1M是,多分配1M长度.也就是?1024*1024
且在字符串缩短的时候不会释放重新分配.
1.2 链表
使用的是一个双向链表.存储的是指针void*
typedef struct listNode { struct listNode *prev; struct listNode *next; void *value; }listNode;
然后,再次封装了上面的链表结构,可以实现多态.
typedef struct list { listNode *head; //指向链表头 listNode *tail; //指向链表尾 unsigned long len; //链表总长 void *(*dup)(void* ptr); //复制节点的函数 void *(free)(void* ptr); //释放节点的函数 int (*match)(void* ptr,void* key); //比较节点与一个值的函数 };
- 链表是双向的
- 无环
- 记录了头尾指针
- 记录了链表长度
- 多态
1.3 字典hash
distht
结构存储一个哈希表,记录了哈希表总大小,掩码,已有节点数量
typedef struct dictht { dictEntry **table; //哈希表数组 unsigned long size; //哈希表大小 unsigned long sizemask; //用于计算索引值, //总是等于 size - 1 unsigned long used; //哈希表已有节点数量 } dictht;
每个节点结构内部记录了,键,值.值使用联合体,是一个8字节大小的,因此可以存储一个指针.同时还有一个指向下一个节点的指针.
typedef struct dictEntry { void *key; //键 union { //值 void *val; uint_64 u64; int64_t s64; } v; sturct dictEntry *next; //指向下个哈希表节点,形成链表 } dictEntry;
字典使用链表法解决冲突
dict
结构进一步封装字典,能够主要是记录类型和方便rehash
typedef struct dict { dictType *type; //类型特定函数 void *privdata; //私有数据 dictht ht[2]; //哈希表,rehash的时候使用另一个 int rehashdx; //rehash 索引,当 rehash 不在进行时,值为-1 } dict;
dictType
用于实现多态
typedef struct dictType { unsigned int (*hashFunction)(const void *key);// 含key的hash函数 void *(*keyDup)(void *privdata, const void *key);// key的拷贝函数 void *(*valDup)(void *privdata, const void *obj);// value的拷贝函数 int (*keyCompare)(void *privdata, const void *key1, const void *key2); // 对key的compare函数 void (*keyDestructor)(void *privdata, void *key);// key析构 void (*valDestructor)(void *privdata, void *obj);// value析构 } dictType;
因此存储过程是
int index = dict->type->hashFunction(key) & dict->ht[x].sizemask;
redis的哈希算法使用的murmurhash
使用了链表发解决冲突
rehash过程
- 如果没有执行bgsave,也就是rdb持久化,那么负载银子大于等于1时执行
- 在执行bgsave,那么负载因子大于等于5时执行.
- 负载因子小于0.1时开始收缩
扩展的大小为上次使用的大小的2倍.然后取2^n
,收缩的则是取已使用取2^n
.都是取的最近接的一个2^n
.
使用的是ht[2]
里面的两个表相互对调的方式.然后再讲ht[2]
两个互换,也就是最终,都是ht[0]
中存储哈希表
渐进式rehash
- 为
ht[1]
分配空间 - rehashidx设置为0,表示rehash开始
- 随后的在增删改查过程,都会将对应的hash值的
ht[0]
中的数据,rehash到ht[1]
中 - 直到最后一个
ht[0]
中的节点被rehash
渐进式rehash的查增删改查会在两个表中依次进行一次.也就是ht[0]
一直在减少.
1.4 跳跃表
使用跳跃表,.代替红黑树和平衡树来实现set.其实就是c++中的map,是一个有序键值对的集合
只在有序集合和集群节点时使用.
1.5 整数集合
就是个扩展的数组.
存储了数组中每个元素所占位数和长度,然后紧跟这一个数组.
数组中每个元素所占位数称为编码.
整数集合中的元素可以由低编码,转换为高编码,但是不能反过来转换,也就是从16位到32可以,反过来不可以.
这个过程称为升级,当天家一个多位的元素是,整个集合都需要升级.
过程为:
- 计算空间,并分配
- 从后向前一次搬运
1.6 压缩列表
是一个嗯...自定义的数组结构,用来存储整数和短字符串.
也就是说一个压缩列表里可以同时存储两种类型的数据.
压缩列表就是一个连续内存区域,头部记录
- 整个内存区域总长度4字节
- 最后一个节点到头部的距离.4字节
- 节点数量
后面根一个个的节点,最后一个特殊值表示结尾
每个节点的格式为:前一个节点的长度(采用1字节或是5字节来揭露),本节点的编码,编码为本节点的数据类型和长度.数据
连锁更新
值得是节点的前一节点长度字段的连锁更新.
当压缩列表中的所有节点都是250~254左右,也就是一个字节能够记录.然后头部插入一个大于254长度的节点,因此后一个节点本来长度字段使用1字节,因此变为5字节,连锁的后面的节点的长度字段都需要跟着更新,成为连锁更新.
最坏复杂度o(n^2)
2 对象类型
编码和对象
redis使用对象来表示数据库中的键和值.当在数据库中创建一个键值对的时候,至少就创建了两个对象:一个键,一个值
- 字符串对象
整数值实现实现
int
embstr的简单动态字符串
embstr
简单动态字符串实现
raw
- 列表对象
压缩列表实现
双端链表实现
linkedlist
- 哈希对象
字典实现
hashtable
压缩列表实现
ziplist
- 集合对象
整数集合实现
intset
字典实现
- 有序集合对象
压缩列表实现
跳跃表和字典实现
skiplist
2.1 字符串对象
浮点数被保存为字符串,计算的时候会先转为浮点数然后在计算,计算完了在以字符串的形式保存.
只有整数会被保存为int,
- set 新建
整数被保存为int,其他被保存为字符串
- get 获取
都转化为raw也就是简单动态字符串,然后返回字符串
没有则nil
- append
转换为raw.raw调用sdscatlen
没有则直接新建
- incrbyfloat 基础上加一个浮点数
只有int类型,可以其他两种报错
没有则新建.
- incrby 整数假发
同上
- decrby
同上
- strlen
转换为字符串,返回长度
- setrange name index value
转换为字符串,改变指定位置上的值
- getrange name start end
转换为字符串,返回给定两个索引作为起始坐标的字符串
2.2 列表对象
ziplist和linkedlist
ziplist的使用条件为
- 每个字符串元素长度小于64
- 元素个数小于512
否则会被转化.redis自动转化
- lpush
压入链表头
- rpush
尾部添加
- lpop
头删除
- rpop
尾删除
- lindex name index
返回指定index的元素
- llen
链表长
- linsert name BEFORE/AFTER 已存在值 要加入的值
指定位置插入,指定的已存在的值,而不是index
- lrem
不懂
- lset name index value
替换,指定的index
2.3 哈希对象
ziplish或是hashtable
转换条件同上
操作
命令 | 作用 |
---|---|
hset obj key value | 插入值 |
hget obj key | 获取值,不存在返回nil |
hexosts obj key | 是否存在,不存在返回0 |
hdel obj key | 删除键值对 |
hlen obj | 获取元素个数 |
hgetall obj |
获取所有 |
2.4 集合对象
intset或是hashtable
转换条件
- 存在元素不是整数值
- 保存元素超过512
命令 | 描述 |
---|---|
sadd obj ... | 添加元素 |
scard obj | 返回元素数量 |
sismember obj value | 是否存在,没有返回0 |
srandmember obj | 随机返回一个数 |
spop obj | 随机返回并删除 |
srem obj value | 删除给顶元素 |
2.5 有序集合
ziplist和skiplish
有序集合使用字典和跳跃表实现的时候,是为了增加性能.集合了两种性能.同事保存,也就是保存了两份数据,一份用哈希表一份用跳跃表
转换条件:
- 元素长度大于64
- 元素个数大于128
命令 | 描述 |
---|---|
zadd obj number value | 插入,key只能是数值,float |
zcard obj | 返回数量 |
zcount obj value1 value2 | 给定value 范围内的数量,value只能是数值,float |
zrange obj index1 index2 | 返回范围内的所有的值 |
zrevramge | 反向饭饭 |
zrank obj value | 返回value所对应key的排名 |
zrevrank | 反向,同上 |
zrem value | 删除执行 |
score obj value |
返回对应的key |
内存回收
redis实现了引用计数的内存回收.
共享对象
服务器初始化穿件了0~9999的一万个字符串,共享这些对象.
使用
OBJECT REFCOUNT name
查看计数,一般只是上面的10000个整数共享了.
对象空转时长
也就是记录从上次访问到现在经过的时间.单位秒
OBJECT IDELTIME name
原文地址:https://www.cnblogs.com/perfy576/p/8605918.html