Redis-数据结构与对象

个人学习redis的笔记和摘抄,主要来自《Reids设计与实现》一书

Redis-对象

对象

redis定义了一系列基本数据结构,并基于这些数据结构创建了一个对象系统,包括String、List、Set、Hash、Zset。

使用对象的好处

  • 针对不同使用场景设置不同的数据结构实现,优化使用效率
  • 基于引用计数的内存回收机制,节约内存;多数据库共享一个对象
  • 对象带有访问时间信息,空转时间较长的被优先删除

对象的定义

typedef struct redisObject {  
  
    // 类型 
    unsigned type:4;          
  
    // 编码方式(数据结构)
    unsigned encoding:4;  
  
    // LRU 时间(相对于 server.lruclock)  
    unsigned lru:22;  
  
    // 引用计数  
    int refcount;  
  
    // 指向对象的值  
    void *ptr;  
  
} robj; 

  • 可以通过TYPE命令查看对象的类型
  • 可以通过OBJECT ENCODING查看对象的编码

不同对象的实现

String

字符串对象有intrawembstr三种

int:整数值且可以用long类型表示

raw:字符串值且长度>39字节,用SDS表示,通过两次内存分配函数分别创建redisObject和sdshdr结构

embstr:字符串值且长度<=39字节,通过一次内存分配函数分配一块连续的空间。

浮点数也是用raw或者embstr表示的,使用会进行转换

转换

当int使用append操作变成str或者embstr大于39字节或转换成raw编码

List

列表对象有ziplistlinkedlist

  • 当所有字符串元素长度小于64字节
  • 元素数量小于512个

满足上述用ziplist,其他用linkedlist(可以设定上限值)

Hash

列表对象有ziplisthashtable

  • 当所有键值对键和值长度小于64字节
  • 元素数量小于512个

满足上述用ziplist(键和值相邻存储在ziplist),其他用hashtable(可以设定上限值)

Set

列表对象有intsethashtable

  • 当所有元素都是整数
  • 元素数量小于512个(可以设定上限值)

满足上述用intset,其他用hashtable,键的值为null

ZSet

列表对象有ziplistskiplist

  • 当所有元素长度小于64字节
  • 元素数量小于128个(可以设定上限值)

满足上述用ziplist,其他用skiplist(还包含一个dict保存成员到分值的映射,可以O(1)求分值)

内存回收

对象保存了引用计数,可以通过引用计数在合适的时间进行回收

对象共享

Redis包含且只包含对整数值的字符串对象进行共享,服务器初始化会创建10000个字符串对象,包含0-9999所有整数值

对象空转时长

lru属性记录了对象最后一次被命令程序访问的时间,一些设置maxmemory后可以使空转较长的对象被回收

Redis-数据结构

SDS

SDS(simple dynamic string)作为Redis的默认字符串表示

定义

struct sdshdr {
    
    // buf 中已占用空间的长度
    int len;

    // buf 中剩余可用空间的长度
    int free;

    // 数据空间
    char buf[];
};

与C字符串区别

  • 常数时间获得字符串长度

  • 杜绝了缓冲区溢出(对于字符串拼接操作,会先检查空间是否足够,不够会首先扩展空间)

  • 减少修改造成的内存重分配次数

    • 空间预分配(扩展时会分配额外空间)
    • 惰性空间释放(缩小时不会立即释放空间)
  • 二进制安全(利用len判断结束而不是\0)

  • 兼容部分C字符串API

链表

一个很典型的双端链表的实现

  • 带头尾指针,O(1)访问头尾节点
  • 带链表长度计数器
  • 顺序访问,增删灵活,缺点就是不能提供随机访问

字典

定义

typedef struct dict {

    // 类型特定函数
    dictType *type;

    // 私有数据
    void *privdata;

    // 哈希表
    dictht ht[2];

    // rehash 索引
    // 当 rehash 不在进行时,值为 -1
    int rehashidx; /* rehashing not in progress if rehashidx == -1 */

    // 目前正在运行的安全迭代器的数量
    int iterators; /* number of iterators currently running */

} dict;

一个字典包含两个hash表,第二个用于扩容rehash时使用

hash表通过开放链地址解决冲突,头插

rehash

和Hashmap类似,扩容或收缩

  • 分配新的空间
  • 重新计算索引位置
  • 释放旧空间,改引用

时机

  • 没有执行BGSAVE或者BGREWRITEAOF命令时,负载因子大于1
  • 执行BGSAVE或者BGREWRITEAOF命令时,负载因子大于5
  • 负载因子小于0.1时,收缩

渐进式rehash

1、为ht[1]分配空间,让字典同时持有ht[0]和ht[1]两张哈希表。

2、将字典中的rehashidx设置成0,表示正在rehash。rehashidx的值默认是-1,表示没有在rehash。

3、在rehash进行期间,程序处理正常对字典进行增删改查以外,还会顺带将ht[0]哈希表上,rehashidx索引上,所有的键值对数据rehash到ht[1],并且rehashidx的值加1。

4、当某个时间节点,全部的ht[0]都迁移到ht[1]后,rehashidx的值重新设定为-1,表示rehash完成。

在此期间的删改查将在两个哈希表上进行,增加在新的ht[1]表

跳跃表

为什么使用跳表而不是红黑树?

  • 跳表和红黑树性能相仿,但跳表实现简单,易于排错
  • 易于实现范围查找,空间局部性能更优

定义

在这里插入图片描述

每个点随机一个层高(高度越大出现几率越小)连接前后

整数集合

定义

typedef struct intset {
    // 编码方式
    uint32_t encoding;
    // 集合包含的元素数量
    uint32_t length;
    // 保存元素的数组
    int8_t contents[];
} intset;

encoding包含16位、32位、64位三种值,数组内元素有序存储

增删操作O(N),查询操作O(logN)

升级

当一个16位编码的整数集合插入一个32位数,就会升级到32位编码

好处:节约内存、提升语言灵活性

压缩列表

定义

压缩列表是Redis为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构。

在这里插入图片描述

连锁更新

压缩列表每个entry有固定大小值,且记录了上一个entry的长度,可能引发连锁更新

发布了3 篇原创文章 · 获赞 0 · 访问量 51
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览