Redis学习之对象系统源码分析
背景知识:
Redis并没有直接使用sds,双端链表,字典,压缩列表,跳表等这些数据结构来直接实现键值对数据库,而是基于这些对象创建了一个对象系统,这个对象系统包含5个对象:字符串对象,列表对象,哈希对象,集合对象和有序集合对象,字符串对象是唯一会被其他四种对象嵌套的对象
1.我们可以针对不同的使用场景,为对象设置多种不同的数据结构,从而优化对象在不同场景下的使用效率
2.Redis的对象系统实现了基于引用计数的内存回收机制
3.Redis的对象系统还实现了对象共享机制,这个机制在适当条件下,通过让多个数据库键共享同一个对象来节约内存
4.Redis的对象带有访问时间记录信息,该信息可以用来计算数据库键的空转时长,在服务器启用了maxmemory功能的情况下,空转时长较大的那些键可能会被服务器优先删除
5.当我们在redis中创建一个新的键值对时,我们至少会创建两个对象,一个对象用作键,另外一个对象用作值
一.Redis对象系统
typedef struct redisObject {
// 类型,键值对中值的类型
unsigned type:;
// 编码,决定采用什么类型的数据结构实现
unsigned encoding:;
// 对象最后一次被访问的时间
unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
// 引用计数
int refcount;
// 指向实际值的指针
void *ptr;
} robj;
通过encodeing属性来设定对象所使用的编码,而不是为特定类型的对象关联一种固定的编码,极大的提升了Redis的灵活性和效率,因为Redis可以更具不同的使用场景来为对象设置一个不同的编码,从而优化对象在某一场景下的效率
比如下面这种情况:
在列表对象包含较少元素时,Redis使用压缩列表作为列表对象的底层实现,因为压缩列表比起双端列表更节约内存,并且在元素数量较少时,在内存中以连续块的方式保存压缩列表比起双端链表可以更快的载入到缓存中,但是!随着元素的增加,使用压缩列表带来的优势越来越小,对象系统就会将列表的底层实现转向功能更强,也更适合保存大佬元素的双端列表上,其他类型对象也会通过使用多种不同的编码来进行类似的优化
下面是 不同对象类型对应的不同编码:
|-- 1.使用整数值实现的字符串对象
|
字符串对象: |-- 2.使用embstr编码的SDS简单动态字符串实现的字符串对象
|
|-- 3.使用SDS简单动态字符串实现的字符串对象
|--1.使用ziplist压缩列表实现的列表对象
列表对象: |
|--2.使用list双端链表实现的列表对象
|--1.使用ziplist压缩列表实现的哈希对象
哈希对象: |
|--2.使用dict字典实现的哈希对象|--1.使用intset整数集合实现的集合对象
集合对象: |
|--2.使用dict字典实现的集合对象
|--1.使用ziplist压缩列表实现的有序集合对象
有序集合对象:|
|--2.使用跳跃表和字典实现的有序集合对象
1.字符串对象
字符串对象的编码可以是int,raw,embstr
1)当字符串对象保存的是整数值时,字符串对象的编码是int
2)当字符串对象保存的是字符串时,并且这个字符串的长度大于39字节,那么字符串对象的编码是raw
3)当字符串对象保存的是字符串时,并且这个字符串的长度小于等于39字节,那么字符串对象的编码是embstr
说明:
embstr编码是专门用于保存短字符串的一种优化编码方式
embstr编码和raw编码的区别:
raw编码会调用两次内存分配函数来分别创建redisobject和sdsstr结构,而embstr编码则通过调用一次内存分配函数来分配一块连续的空间,空间中依次包含redisobject和sdsstr两个结构
使用embstr编码来保存短字符串值有以下两个好处:
1)调用内存分配函数的次数减少了一次,并且调用内存释放函数的次数也减少了一次
2)embstr编码的字符串对象的所有数据都保存在一块连续的内存中,这样可以更好的利用缓存带来的优势、
ps:浮点类型的数据也是通过字符串值来保存的
编码可以进行特定的相互转换
2.列表对象
1.列表对象的编码可以是ziplist压缩列表或者linkedlist链表


问题:列表对象什么时候采用ziplist压缩列表编码,什么时候采用linkedlist链表编码?
当列表对象满足下面两个条件的时候,采用ziplist压缩列表进行编码
1)列表对象保存的所有字符串元素长度小于64字节
2)列表保存的元素数量小于512个
若不能通过满足上面两个条件的话列表将采用双端链表编码
3.哈希对象
哈希对象的编码可以是ziplist压缩列表或hashtable哈希表
当哈希对象采用ziplist压缩列表进行编码的时候:
保存了同一键值对的两个结点总是紧紧挨在一起,保存键的结点在前,保存值的结点在后
当哈希对象满足下面两个条件时,哈希对象采用ziplist压缩列表进行编码:
1)哈希对象保存的键值对的字符串长度都小于64字节
2)噶厦镀锡保存的键值对数量小于512个
当不满足上面两个条件时会进行编码转换采用hashtable进行编码
4.集合对象
集合对象的编码可以是intset整数集合或者hashtable哈希表
当集合对象满足下面两个条件时,集合对象采用intset编码:1)集合对象保存的所有元素都是整数值
2)集合对象保存的元素数量不超过512个
如果不满足上面两个条件,集合对象将采用hashtable哈希表进行编码
5.有序集合对象
有序集合对象的编码可以是ziplist或者skiplist
ziplist编码:ziplist结构
skiplist编码:skiplist结构和dict结构
当有序集合对象满足下面两个条件时,有序集合对象采用ziplist编码
1)有序集合保存的元素数量小于128个
2)有序集合保存的所有元素成员长度小于64字节
否则的话,有序集合对象将采取skiplist编码,底层用skiplist和dict数据结构实现
关于有序集合对象采用skiplist编码的说明:
在采用skiplist编码的时候,其底层采用的是skiplist跳表和dict字典
1.通过跳跃表,有序集合可以高效的进行范围性操作
2.通过字典,有序集合可以高效的查找成员到分值的映射
值得一提的是,虽然有序集合对象在采用skiplist编码的时候,同时采用了跳跃表和字典来保存原始,但是着两种数据结构都会通过指针来共享相同原始的成员和分值,不会因此而浪费额外的内存来保存相同元素
为什么有序集合在采用skiplist编码的时候,需要通过采用跳表和字典来进行底层的实现?
1)采用字典,不采用跳表:虽然查找成员到分值的映射的复杂度为O(1),但是执行比如ZRANK命令时,程序需要对字典保存的所有元素进行排序,至少需要O(log N),以及额外的O(N)内存空间
2)采用跳表,不采用字典:虽然执行范围性操作的时间复杂度是O(log N),但是根据成员查找映射的分值的操作的复杂度将从O(1)上升到O(log N)
所以为了让有序集合的查找和范围性操作都尽可能的快速执行,Redis选择同时使用字典和跳跃表两种数据结构来实现有序集合,同时有序集合中的字典和跳跃表会共享元素的成员和分值,所以并不会造成数据重复,也不会因此浪费任何内存
Redis的内存回收
1.Redis采用引用计数法进行内存回收,该方法最大的问题就是不能解决互相引用的问题
2.对象的空转时长:当前时间减去该对象最后一次被访问的时间就是该对象的空转时长,空转时长较长的对象在内存不够时会被回收
Redis的对象共享
采用对象共享机制是为了节约内存,数据库保存的相同值的对象越多,对象共享机制就节约越多的内存
目前来说,Redis会在初始化服务器时,创建一万个字符串对象,这些对象包含了从0到9999的所有整数值,当服务器需要用到值为0到9999的字符串对象时,服务器就会共享这些对象,而不是创建新对象
需要注意的是
尽管共享复杂的对象可以节约更多的内存,但是复杂的对象比较起来也比较耗时间,比如如果共享的对象包含了多个值的对象,那么比较两个对象的复杂度为O(N*N),这个比较操作非常耗费CPU时间,所以收到CPU时间的现在,Redis只多包含整数值的字符串对象进行共享
总结:
1)Redis数据节中每个键值对的键和值都是一个对象
2)Redis共有字符串对象,列表对象,哈希对象,集合对象,有序集合对象,这五种对象,每种类型的对象至少都有两种以上的编码方式,不同的编码方式可以在不同的使用场景上使用,可以优化对象的使用效率
3)服务器在执行某些命令前,需要先检查给定的键是否可以执行指定的命令,而检查一个键的类型就是检查键对应的值的对象类型
Redis学习之对象系统源码分析的更多相关文章
- Redis学习之底层链表源码分析
Redis底层链表的源码分析: 一.链表结点的结构(单个结点): // listNode 双端链表节点 typedef struct listNode { // 前置节点 struct listNod ...
- Android12系统源码分析:NativeTombstoneManager
Android12系统源码分析:NativeTombstoneManager 概述 android12新增的system_server进程(LocalService)本地服务,用于管理native t ...
- memcached学习笔记——存储命令源码分析下篇
上一篇回顾:<memcached学习笔记——存储命令源码分析上篇>通过分析memcached的存储命令源码的过程,了解了memcached如何解析文本命令和mencached的内存管理机制 ...
- memcached学习笔记——存储命令源码分析上篇
原创文章,转载请标明,谢谢. 上一篇分析过memcached的连接模型,了解memcached是如何高效处理客户端连接,这一篇分析memcached源码中的process_update_command ...
- Redis 专栏(使用介绍、源码分析、常见问题...)
一.介绍相关 说Redis : 介绍Redis特性,使用场景,使用Jedis操作Redis等. 二.源码分析 1. 数据结构 Redis源码分析(sds):Redis自己封装的C语言字符串类型. Re ...
- Vue3中的响应式对象Reactive源码分析
Vue3中的响应式对象Reactive源码分析 ReactiveEffect.js 中的 trackEffects函数 及 ReactiveEffect类 在Ref随笔中已经介绍,在本文中不做赘述 本 ...
- tornado 学习笔记6 Application 源码分析
Application 是Tornado重要的模块之一,主要是配置访问路由表及其他应用参数的设置. 源代码位于虚拟运行环境文件夹下(我的是env),具体位置为env > lib>sit-p ...
- jQuery Deferred对象详细源码分析(-)
本系列文章讲介绍这个Deferred东西到底拿来干什么,从1.5版本加进来,jQuery的很多代码都重写了.直接先上源码分析了,清楚了源码分析,下节将讲具体的应用 以及应用场景. 创建对象 var d ...
- Android FrameWork 学习之Android 系统源码调试
这是很久以前访问掘金的时候 无意间看到的一个关于Android的文章,作者更细心,分阶段的将学习步骤记录在自己博客中,我觉得很有用,想作为分享同时也是留下自己知识的一些欠缺收藏起来,今后做项目的时候会 ...
随机推荐
- Python struct与小端存储
参考链接:https://www.liaoxuefeng.com/wiki/1016959663602400/1017685387246080 在使用Python 实现字符向字节数据类型转换的时候,P ...
- Linux入门——初识Linux
Linux入门——初识Linux 摘要:本文主要说明了Linux是什么,Linux发展历史,以及同Linux系统有关的一些基本知识. 简介 操作系统 Linux系统同Windows系统.Mac系统一样 ...
- Java面向对象——相关基本定义
Java面向对象——相关基本定义 摘要:本文简单介绍了面向对象的编程方式,以及与之有关的一些基本定义. 面向对象 什么是面向对象 面向对象编程是一种对现实世界建立计算机模型的一种编程方法.简称OOP( ...
- .NET开发框架(六)-架构设计之IIS负载均衡(视频)
前面有关注我们公众号文章的朋友应该都知道,我们的分布式应用服务可以通过Ocelot网关进行负载均衡,这种方式属于应用级别的实现. 而今天我们给大家介绍的是平台级别的实现,并且我们首次使用视频方式进行讲 ...
- canvas的常用功能(电脑版)
前言: canvas可以单独算为前端的一大知识模块, 今天就研究一下. 先做下前文铺垫: ①创建canvas <canvas id="myCanvas" width=&quo ...
- python中pip添加国内镜像源后显著加速下载
python中pip添加国内镜像源后显著加速下载 更换pip源到国内镜像,很多国外的库下载非常慢,添加国内镜像后安装下载速度提升非常明显(亲测有些可以由几十kb加速到几MB) pip国内的一些镜像阿里 ...
- 第九届极客大挑战——怎么又是江师傅的秘密(java反序列化)
这道题其实是考jsp和java的,我没学过jsp,java倒是有一点了解,但是刚拿到题的时候还是看不懂java代码里的内容,所以去简单学习了下jsp(jsp教程),按照教程里的步骤搭建了eclipse ...
- 每次都能让人头大的 Shader -- 从整合说起
之前也说过引擎能不能提供一个一般化的开发环境给使用者, 这样使用者只需要指定他要的开发环境, 就能用它最熟悉的方式去写Shader了. 从提供者的角度来看, 因为有太多的应用场景无法确定, 所以提供无 ...
- Mac环境下 jieba 配置记录
在终端里输入: pip3 install jieba -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
- eclipse打可运行的jar
参考:https://www.cnblogs.com/wangzhisdu/p/7832666.html 用eclipse打包可运行的jar比较坑的地方: 3.1 从下拉框选择该jar的入口文件,即m ...











