Redis并无直接使用内部的基本数据结构来实现键值对数据库,而是基于这些数据结构建立了一个对象系统,这个系统包含字符串对象、列表对象、哈希对象、集合对象和有序集合对象这五种类型的对象, 每种对象都用到了至少一种内部的基本数据结构。
一.对象的结构node
typedef struct redisObject { unsigned type;// 类型 unsigned encoding;// 编码 void *ptr;// 指向底层实现数据结构的指针 int refcount; // 引用计数 unsigned lru; //该属性记录了对象最后一次被命令程序访问的时间 } robj;
(1).type属性redis
类型常量 | 对象的名称 |
---|---|
REDIS_STRING | 字符串对象 |
REDIS_LIST | 列表对象 |
REDIS_HASH | 哈希对象 |
REDIS_SET | 集合对象 |
REDIS_ZSET | 有序集合对象 |
(2).encoding属性数据库
编码常量 | 编码所对应的底层数据结构 |
---|---|
REDIS_ENCODING_INT | long 类型的整数 |
REDIS_ENCODING_EMBSTR | embstr 编码的简单动态字符串 |
REDIS_ENCODING_RAW | 简单动态字符串 |
REDIS_ENCODING_HT | 字典 |
REDIS_ENCODING_LINKEDLIST | 链表 |
REDIS_ENCODING_ZIPLIST | 压缩列表 |
REDIS_ENCODING_INTSET | 整数集合 |
REDIS_ENCODING_SKIPLIST | 跳跃表 |
(3).refcount服务器
内存回收:由于C语言并不具有自动的内存回收功能,因此Redis在本身的对象系统中构建了一个引用计数(reference counting)技术实现的内存回收机制,经过这一机制,程序能够经过跟踪对象的引用计数信息,在适当的时候自动释放对象并进行内存回收。
a.在建立一个新对象时, 引用计数的值会被初始化为 1 ; b.当对象被一个新程序使用时, 它的引用计数值会被增一; c.当对象再也不被一个程序使用时, 它的引用计数值会被减一; d.当对象的引用计数值变为 0 时, 对象所占用的内存会被释放。
对象共享:经过引用技术无需对相同对象从新分配内存,而是直接经过对象共享相同的内存块,共享对象机制对于节约内存很是有帮助, 数据库中保存的相同值对象越多, 对象共享机制就能节约越多的内存。
目前来讲,Redis会在初始化服务器时,建立一万个字符串对象,这些对象包含了从0到9999的全部整数值,当服务器须要用到值为0到9999的字符串对象时,服务器就会使用这些共享对象,而不是新建立对象。
a.将数据库键的值指针指向一个现有的值对象; b.将被共享的值对象的引用计数增一。
Redis只对包含整数值的字符串对象进行共享。
(3).lru数据结构
空转时长就是经过将当前时间减去键的值对象的 lru 时间计算得出;能够用于回收内存(当开启maxmemory选项时),空转时长较高的那部分键会优先被服务器释放
volatile-lru:从已设置过时时间的数据集中挑选最近最少使用的数据淘汰 volatile-ttl:从已设置过时时间的数据集中挑选将要过时的数据淘汰 volatile-random:从已设置过时时间的数据集中任意选择数据淘汰 allkeys-lru:从数据集中挑选最近最少使用的数据淘汰 allkeys-random:从数据集中任意选择数据淘汰 no-enviction(驱逐):禁止驱逐数据,只是发出警告
二.字符串对象(REDIS_STRING)dom
a.若是一个字符串对象保存的是整数值,而且这个整数值能够用long类型来表示,那么字符串对象会将整数值保存在字 符串对象结构的 ptr属性里面(将 void* 转换成 long ),并将字符串对象的编码设置为 int b.若是字符串对象保存的是一个字符串值,而且这个字符串值的长度大于39字节,那么字符串对象将使用一个简单动态 字符串(SDS)来保存这个字符串值,并将对象的编码设置为 raw c.若是字符串对象保存的是一个字符串值,而且这个字符串值的长度小于等于39字节,那么字符串对象将使用 embstr 编码的方式来保存这个字符串值。(embstr能够连续分配内存)
(1).编码的转换编码
a.对于int编码的字符串对象来讲,若是咱们向对象执行了一些命令,使得这个对象保存的再也不是整数值,而是一个字 符串值,那么字符串对象的编码将从int变为raw b.由于Redis没有为embstr编码的字符串对象编写任何相应的修改程序 (只有 int 编码的字符串对象和 raw 编码的字 符串对象有这些程序),因此 embstr 编码的字符串对象其实是只读的:当咱们对 embstr 编码的字符串对象执行任 何修改命令时,程序会先将对象的编码从 embstr 转换成 raw ,而后再执行修改命令; 由于这个缘由,embstr 编码 的字符串对象在执行修改命令以后,总会变成一个 raw 编码的字符串对象。
三.列表对象(REDIS_LIST)spa
a.当一个列表对象只包含少许列表项,而且每一个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis 就会使用压缩列表来作列表键的底层实现。 b.当一个列表对象包含较多的列表项,那么Redis就会使用链表结构做为底层实现, 每一个双端链表节点(node)都保存 了一个字符串对象, 而每一个字符串对象都保存了一个列表元素。
(1).编码转换指针
a.当列表对象能够同时知足如下两个条件时, 列表对象使用 ziplist 编码: 列表对象保存的全部字符串元素的长度都小于 64 字节; 列表对象保存的元素数量小于 512 个; b.不能知足这两个条件的列表对象须要使用 linkedlist 编码。
四.哈希对象(REDIS_HASH)code
a.当一个哈希对象只包含少许键值对,而且每一个键值对的键和值要么就是小整数值,要么就是长度比较短的字符串,那 么 Redis 就会使用压缩列表来作哈希键的底层实现。 b.当一个哈希对象包含较多键值对,哈希对象使用字典做为底层实现
(1).编码转换
a.当哈希对象能够同时知足如下两个条件时, 哈希对象使用 ziplist 编码: 哈希对象保存的全部键值对的键和值的字符串长度都小于 64 字节; 哈希对象保存的键值对数量小于 512 个; b.不能知足这两个条件的哈希对象须要使用 hashtable 编码。
五.集合对象(REDIS_SET)
a.当一个集合对象只包含整数值元素, 而且这个集合对象的元素数量很少时, Redis就会使用整数集合做为集合对象 的底层实现。 b.当一个集合对象包含非整数值元素,或者元素数量较多时,Redis就会使用字典做为底层实现
(1).编码的转换
a.当集合对象能够同时知足如下两个条件时, 对象使用 intset 编码: 集合对象保存的全部元素都是整数值; 集合对象保存的元素数量不超过 512 个; b.不能知足这两个条件的集合对象须要使用 hashtable 编码。
六.有序集合对象(REDIS_ZSET)
a.当一个有序集合对象只包含少许列表项,而且每一个列表项要么就是小整数值,要么就是长度比较短的字符串,那么 Redis就会使用压缩列表来作有序集合的底层实现,压缩列表内的集合元素按分值从小到大进行排序, 分值较小的元素 被放置在靠近表头的方向, 而分值较大的元素则被放置在靠近表尾的方向。 b.当一个集合对象包含较多元素,Redis就会使用跳跃表与字典做为底层实现。dict字典为有序集合建立了一个从成员 到分值的映射,经过这个字典, 程序能够用 O(1) 复杂度查找给定成员的分值。zsl跳跃表按分值从小到大保存了全部 集合元素,跳跃表节点的 object 属性保存了元素的成员, 而跳跃表节点的 score 属性则保存了元素的分值。 经过 这个跳跃表, 程序能够对有序集合进行范围型操做,
(1).编码的转换
a.当有序集合对象能够同时知足如下两个条件时, 对象使用 ziplist 编码: 有序集合保存的元素数量小于 128 个; 有序集合保存的全部元素成员的长度都小于 64 字节; b.不能知足以上两个条件的有序集合对象将使用 skiplist 编码。