Redis对象的设计与实现
一、Redis对象结构
Redis中的每个对象都由一个redisObject结构表示:
typedef struct redisObject {
unsigned type;//类型
unsigned encoding;//编码
void *ptr;//指向底层实现数据结构的指针
int refcount;//引用计数
unsigned lru;//对象最后一次被程序访问的时间
}
1. type:Redis对象类型
redisObject的'type'属性记录了对象的类型:
type命令:返回键对应的值对象的类型。
2. encoding:Redis对象编码和底层实现
redisObject的'encoding'属性记录了对象所使用的编码(记录对象底层实现的数据结构):
每种类型的对象都至少使用了两种不同的编码:
object encoding命令:查看一个数据库键的值对象的编码。
通过'encoding'属性来设定Redis对象所使用的编码,而不是为特定类型的对象关联一种固定的编码,提升了Redis的灵活性和效率,因为Redis可以根据不同的使用场景来为一个对象设置不同的编码, 从而优化对象在某一场景下的效率。
Redis服务器执行一个命令时,首先通过'type'属性进行类型检查,判断对象类型是否支持当前命令,然后再根据'encoding'属性判断对象的编码方式,选择正确的命令实现代码来执行命令。
3. refcount:Redis对象引用计数
Redis对象通过其'refcount'属性实现对象内存回收与对象共享。当Redis需要创建一个键值对的时候,如果已经存在一个与需要创建的值对象完全相同的值对象,Redis不会创建一个新的值对象,而是将键对象的值指针指向这个现有的完全相同的值对象,同时将这个值对象的'refcount'属性值加一,从而实现对象共享,节约内存;当一个键对象不再指向这个值对象的时候,则值对象的'refcount'属性值减一,当'refcount'属性值为0的时候,则将对象释放,将内存回收。
目前,Redis会在初始化服务器时,创建一万个字符串对象,这些对象包含了从0到9999的所有整数值,当服务器需要用到值为0到9999的字符串对象时,服务器就会使用这些共享对象,而不是新创建对象。创建共享字符串对象的数量可以通过修改redis.h/REDIS_SHARED_INTEGERS常量来修改。
当服务器考虑将一个共享对象设置为键的值对象时,程序会先验证给定的共享对象和键想创建的目标对象是否完全相同,共享对象保存的值越复杂,验证所需的复杂度就越高,消耗的CPU时间也会越多,所以Redis只对包含整数值的字符串对象进行共享,其验证复杂度为O(1)。
引用计数查看命令:object refcount
4. lru:Redis对象最后一次被程序访问的时间
对象的空转时长 = 当前时间 - 对象的'lru'属性值
查看对象空转时长的命令:object idletime(这个命令不会改变对象的'lru'属性值)
如果服务器打开了maxmemory选项,并且服务器用于回收内存的算法为volatile-lru或者allkeys-lru,那么当服务器占用的内存数超过了maxmemory选项所设置的上限值时,空转时长较高的那部分键会优先被服务器释放,从而回收内存(可以参考配置文件的maxmemory选项和maxmemory-policy选项)。
二、字符串对象
1. 字符串对象根据其值类型的不同,使用不同的编码方式,对应关系如下:
(1)可以用long类型表示的整数值:int
(2)小于等于39字节的字符串值:embstr(底层结构为embstr编码的SDS)
(3)大于39字节的字符串值:raw(底层结构为SDS)
注:浮点数在Redis中也是作为字符串值来保存的,只是在有需要的时候会将其转化为浮点数值,执行某些操作之后,存入Redis时又转换为字符串值。
2. 关于embstr编码
embstr编码是专门用于保存短字符串的一种优化编码方式,这种编码和raw编码一样,都使用redisObject结构和sdshdr结构来表示字符串对象,但raw编码会调用两次内存分配函数来分别创建redisObject结构和sdshdr结构,而embstr编码则通过调用一次内存分配函数来分配一块连续的空间,空间中依次包含redisObject和sdshdr两个结构。
使用embstr编码的字符串对象来保存短字符串值的好处:
(1)embst 编码将创建字符串对象所需的内存分配次数从raw编码的两次降低为一次。
(2)释放embstr编码的字符串对象只需要调用一次内存释放函数,而释放raw编码的字符串对象需要调用两次内存释放函数。
(3)因为embstr编码的字符串对象的所有数据都保存在一块连续的内存里面,所以这种编码的字符串对象比起raw编码的字符串对象能够更好地利用缓存带来的优势。
3. 三种编码的字符串对象结构
(1)int编码的字符串对象结构示例:
(2)raw编码的字符串对象结构示例:
(3)embstr编码的字符串对象结构示例:
4. 编码的转换
int编码的字符串对象和embstr编码的字符串对象在条件满足的情况下,会被转换为raw编码的字符串对象。
另外,因为Redis没有为embstr编码的字符串对象编写任何相应的修改程序(只有int编码的字符串对象和raw编码的字符串对象有这些程序),所以embstr编码的字符串对象实际上是只读的:当我们对embstr编码的字符串对象执行任何修改命令时,程序会先将对象的编码从embstr转换成raw,然后再执行修改命令;因为这个原因,embstr编码的字符串对象在执行修改命令之后,总会变成一个raw编码的字符串对象。
三、列表对象
1. 列表对象的编码可以是ziplist(底层结构为压缩列表)或者linkedlist(底层结构为双端链表)。
2. 列表对象结构
(1)使用ziplist编码的列表对象结构示例:
(2)使用linkedlist编码的列表对象结构示例:
其中双端链表中又嵌套了多个字符串对象,这里的‘StringObject’只是简化表示。
3. 编码转换
当列表对象可以同时满足以下两个条件时, 列表对象使用ziplist编码:
(1)列表对象保存的所有字符串元素的长度都小于64字节(可由参数list-max-ziplist-value配置);
(2)列表对象保存的元素数量小于512个(可由参数list-max-ziplist-entries配置);
不能同时满足这两个条件的列表对象需要使用linkedlist编码。
四、哈希对象
1. 哈希对象的编码可以是ziplist(底层结构为压缩列表)或hashtable(底层结构为字典)。
2. 哈希对象结构
(1)使用ziplist编码的哈希对象结构示例:
其中压缩列表结构示例如下:
(2)使用hashtable编码的哈希对象结构示例:
3. 编码转换
当哈希对象可以同时满足以下两个条件时, 哈希对象使用ziplist编码:
(1)哈希对象保存的所有键值对的键和值的字符串长度都小于64字节(可由参数hash-max-ziplist-value配置);
(2)哈希对象保存的键值对数量小于512个(可由参数hash-max-ziplist-entries配置);
不能同时满足这两个条件的哈希对象需要使用hashtable编码。
五、集合对象
1. 集合对象的编码可以是intset(底层结构是整数集合)或hashtable(底层结构是字典)。
2. 集合对象结构
(1)使用intset编码的集合对象结构示例:
(2)使用hashtable编码的集合对象结构示例:
(使用字典键值对中的键来存储集合元素,而值都被设置为NULL)
3. 编码转换
当集合对象可以同时满足以下两个条件时,对象使用intset编码:
(1)集合对象保存的所有元素都是整数值;
(2)集合对象保存的元素数量不超过512个(可由参数set-max-intset-entries配置);
不能满足这两个条件的集合对象需要使用hashtable编码。
六、有序集合对象
1. 有序集合的编码可以是ziplist(底层结构为压缩列表)或skiplist(底层结构为跳跃表+字典)。
2. 有序集合对象结构
(1)使用ziplist编码的有序集合对象结构示例:
其中压缩列表结构示例如下:
(2)使用skiplist编码的有序集合对象
使用skiplist编码的有序集合对象同时使用一个字典和一个跳跃表来实现。
结构定义:
结构示例:
其中zset结构示例如下:
理论上有序集合完全可以单独使用字典或跳跃表其中一种数据结构来实现,但是性能上比同时使用这两种数据结构都会有所下降。使用跳跃表可以提高对有序集合进行范围型操作的效率,而使用字典则可以快速找到指定成员的分值,只使用其中一种数据结构会丢失另一种数据结构在对应操作上带来的效率。另外,有序集合中的字典和跳跃表会通过指针共享相同元素的成员和分值,并不会造成任何数据重复,也不会浪费额外内存。
3. 编码转换
当有序集合对象可以同时满足以下两个条件时, 对象使用ziplist编码:
(1)有序集合保存的元素数量小于128个(可由参数zset-max-ziplist-entries配置);
(2)有序集合保存的所有元素成员的长度都小于64字节(可由参数zset-max-ziplist-value配置);
不能满足以上两个条件的有序集合对象将使用skiplist编码。
Redis对象的设计与实现的更多相关文章
- Redis集群设计原理
---恢复内容开始--- Redis集群设计包括2部分:哈希Slot和节点主从,本篇博文通过3张图来搞明白Redis的集群设计. 节点主从: 主从设计不算什么新鲜玩意,在数据库中我们也经常用主从来做读 ...
- 面试官:你了解过Redis对象底层实现吗
上一章我们讲了Redis的底层数据结构,不了解的人可能会有疑问:这个和平时用的五大对象有啥关系呢?这一章我们就主要解释他们所建立的联系. 看这个文件之前,如果对ziplist.skiplist.int ...
- Redis之对象篇——Redis对象系统简介
Redis之对象篇--Redis对象系统简介 前言 之前几篇文章,简单介绍 Redis用到的所有主要数据结构,简单动态字符串(SDS).双端链表.字典.压缩列表.整数集合.跳跃表. 图解Red ...
- Redis对象——字符串
文章导航-readme 前言 上一篇文章Redis之对象篇--Redis对象系统简介简单介绍了Redis的对象系统.Redis使用对象来表示数据库中的键和值每个对象都由一个redisObjec ...
- Redis键值设计(转载)
参考资料:https://blog.csdn.net/iloveyin/article/details/7105181 丰富的数据结构使得redis的设计非常的有趣.不像关系型数据库那样,DEV和DB ...
- 【集群】Redis集群设计原理
Redis集群设计包括2部分:哈希Slot和节点主从 节点主从: 主从设计不算什么新鲜玩意,在数据库中我们也经常用主从来做读写分离,直接上图: 图上能看得到的信息: 1, 只有1个Master,可以有 ...
- 三张图秒懂Redis集群设计原理
转载Redis Cluster原理 转载https://blog.csdn.net/yejingtao703/article/details/78484151 redis集群部署方式: 单机 主从 r ...
- 【redis源码阅读】redis对象
结构定义 在redis中,对象的数据结构定义如下: typedef struct redisObject { unsigned type:4; unsgined encoding:4; uns ...
- Redis对象类型
Redis对象类型 Redis基于基础的数据结构创建的对象: 字符串对象. 列表对象. 哈希对象. 集合对象 有序集合对象. 对象回收:Redis对象系统实现了基于引用计数技术的内存回收机制,当程序不 ...
随机推荐
- python_112_断言
#断言 如果满足断言的执行程序,如果不满足则抛错误 assert type(1) is int print('断言正确的话,就继续执行') # assert type('a') is int #Ass ...
- eclipse 在写XML时 包类名自动提醒的问题
需要加一个STS插件 配置很简单 参考了 https://blog.csdn.net/HH775313602/article/details/70176531 在 https://spring.io ...
- java在线聊天项目 swt可视化窗口Design 重新设计好友列表窗口 增加菜单栏
增加的菜单栏效果图如下: eclipse 中调整到 swt的design视图下 控件区域选择Menu Controls 将Menu Bar拖动到窗口标题栏 将Cascaded Menu拖动到Menu ...
- PLAYGROUND 可视化
PLAYGROUND 可视化 由 王巍 (@ONEVCAT) 发布于 2015/09/23 在程序界,很多小伙伴都会对研究排序算法情有独钟,并且试图将排序执行的过程可视化,以便让大家更清晰直观地了解算 ...
- POJ 3080 Blue Jeans、POJ 3461 Oulipo——KMP应用
题目:POJ3080 http://poj.org/problem?id=3080 题意:对于输入的文本串,输出最长的公共子串,如果长度相同,输出字典序最小的. 这题数据量很小,用暴力也是16ms,用 ...
- POJ-3050-Hoscotch
这是一道简单的深搜题目,题意说的是给一个5*5的棋盘,里面填满数字,然后跳到一个格子上,这是第一步,接着向上下左右四个方向任意一个方向走一步,一共走6步,问我们走过的数字组成的一个6位数有多少种不同的 ...
- python 多线程 压测 mysql
#!/usr/bin/env python # encoding: utf-8 #@author: 东哥加油 #@file: sthread.py #@time: 2018/9/17 17:07 im ...
- Springboot(一)-IDEA搭建springboot项目(demo)
jdk版本:1.8.0_162 1.打开IDEA-file-new-project-Spring Initializer,JDK和URL选默认,next (这一步如果是不能联网的话,可以选择直接创建m ...
- php各种主流框架的优缺点总结
ThinkPHP ThinkPHP(FCS)是一个轻量级的中型框架,是从Java的Struts结构移植过来的中文PHP开发框架.它使用面向对象的开发结构和MVC模式,并且模拟实现了Struts的标签库 ...
- Python多版本共存安装
Python的安装 进入Python官方网站:www.python.org下载系统对应的Python版本 按照提示步奏安装,安装路径选择自定义,方便查找 安装完成后,按win+R键,输入cmd进入cm ...