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的文章,作者更细心,分阶段的将学习步骤记录在自己博客中,我觉得很有用,想作为分享同时也是留下自己知识的一些欠缺收藏起来,今后做项目的时候会 ...
随机推荐
- c++实现文件复制并修改相应属性
问题描述 完成一个目录复制命令mycp,包括目录下的文件和子目录, 运行结果如下: beta@bugs.com [~/]# ls –la sem total 56 drwxr-xr-x 3 beta ...
- Java自学-集合框架 LinkedList
Java集合框架 LinkedList 序列分先进先出FIFO,先进后出FILO FIFO在Java中又叫Queue 队列 FILO在Java中又叫Stack 栈 示例 1 : LinkedList ...
- Vue笔记1
index.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> &l ...
- 听说看了这篇文章就彻底搞懂了什么是OPC(上)
从2000年初以来,我们就一直在使用OPC软件互操作性标准,而那些正准备踏入和想要踏入工业自动化领域的人们却对这些含义感到困惑. 所以在本中,我将系统地为你梳理OPC知识. OPC首字母缩写词代表什么 ...
- 白话SCRUM之一:SCRUM 的三个角色
在SCRUM方法中将项目的利益相关者分成两大类:Pigs角色与chickens角色,pigs即为项目组的实际参与人员,chickens为项目组的外部人员,包括经理.最终用户等等.Pigs在scrum中 ...
- 155--MinStack
/* 解法一:使用链表从0实现栈,用min来存放最小值. 复杂的地方是,如果pop了最小的数,就要遍重新找到最小的数. */ public class MinStack { List<Integ ...
- Django ajax 简单介绍
AJAX Asynchronous Javascript And XML是 "异步Javascript和XML".即使用 Javascript 语言与服务器进行异步交互,传输的数据 ...
- Oracle使用命令行登录提示ERROR: ORA-01017: invalid username/password; logon denied
刚在Windows上面安装好Oracle 10g,刚开始使用PLSQLDevelop软件登录提示 not logged on ,然后使用命令行登录提示 ERROR: ORA-01017: inval ...
- 第五篇:Python函数基础篇
本篇介绍什么是函数.函数的特性.函数的定义.函数的调用.以及函数的参数.以及关于全局变量和局部变量的使用等等. 一.什么是函数: 函数是最基本的一种代码抽象方式,为了实现某种特定的功能而组织的带名字的 ...
- Maven+SSM框架,实现单表简单的增删改查
目录 1.创建web Maven项目 2.创建java源码文件和resources资源文件 3.创建数据库配置文件:jdbc.properties 4.项目总体目录: 5.添加spring配置文件:a ...











