首页 > 数据库 >Redis的五大数据类型的底层实现

Redis的五大数据类型的底层实现

时间:2022-10-28 13:03:02浏览次数:60  
标签:编码 对象 数据类型 ziplist Redis 集合 内存 字符串 底层


Redis的五大数据类型的底层实现

redis是以键值对储存数据的,所以对象又分为对象喝键值对象即, 存储一个key-value键值对会创建两个对象,键对象和值对象。

对象可以是5大对象中的一种

  • type属性存储的是对象的类型,也就是我们说的​​string、list、hash、set、zset​​中的一种,可以使用命令 TYPE key 来查看。
  • encoding属性记录了队形所使用的编码,即这个对象底层使用哪种数据结构实现。
  • Redis的五大数据类型的底层实现_键值对

  • ​我们在存入key-value键值对时并不会对指定对象的encoding,而是Redis会根据不同的场景来为一个对设置不同的编码,可以达到节约内存,加快访问速度等目的​

String字符串对象

字符串对象底层数据结构实现为简单动态字符串(SDS)和直接存储,但其编码方式可以是int、raw或者embstr,区别在于内存结构的不同

  1. int 字符串保存的是整数值
    正式可以用long类型来表示,那么其就会直接保存在redisObject的ptr属性里,并将编码设置为int
  2. raw编码 字符串保存大于32字节的字符串值,
    则使用简单动态字符串(SDS)结构,并将编码设置为raw,此时内存结构与SDS结构一致,内存分配次数为两次,创建redisObject对象和sdshdr结构
  3. embstr编码 字符串保存的小于等于32字节的字符串值
    使用的也是简单的动态字符串(SDS结构),但是内存结构做了优化,用于保存顿消的字符串,内存分配也只需要一次就可以完成,分配的一块连续的空间即可
  • 在Redis中,存储long、double类型的浮点数是先转换为字符串再进行存储的。
  • raw与embstr编码效果是相同的,不同在于内存分配与释放,raw两次,embstr一次。
  • embstr内存块连续,能更好的利用缓存在来的优势
  • int编码和embStr编码如果追加字符串等操作,满足条件转换为raw编码,embstr编码的对象是只读的,一旦修改会先转码到raw。

列表对象(list)

列表对象的编码可以是ziplist和linkedlist之一。

  • ziplist编码

ziplist编码的哈希是底层实现是压缩列表,每个压缩列表节点保存了一个列元素

  • linkedlist编码

linkedlist编码底层采用双端链表实现,每个双端链表节点都保存了一个字符串对象,在每个字符串对象内保存了一个列表元素

  • 列表对象使用ziplist编码需要满足两个条件:一是所有字符串长度都小于64字节,二是元素数量小于512个,不满足任意一个都会使用linkedlist编码
  • 列表对象使用ziplist编码需要满足两个条件:一是所有字符串长度都小于64字节,二是元素数量小于512,不满足任意一个都会使用linkedlist编码。

哈希对象(hash)

哈希对象的编码可以是ziplist和hashtable之一。

  1. ziplist编码
    ziplist编码的哈希对象底层实现是压缩列表,在ziplist编码的哈希对象中,key-value键值对是以紧密相连的方式放入压缩链表的,先把key放入表尾,再放入value;键值对总是向表尾添加。
  2. hashtable编码
    hashtable编码的哈希对象底层实现是字典,哈希对象中的每个key-value对都使用一个字典键值对来保存。
    字典键值对即是,字典的键和值都是字符串对象,字典的键保存key-value的key,字典的值保存key-value的value。

哈希对象编码转换:

  1. 哈希对象使用ziplist编码需要满足两个条件:1 所有键值对的键和值的字符串长度都小于64字节,2 是 键值对数量小于512个 不满足任意一个都是用hashtable编码
  2. 以上两个条件可以在Reids配置文件中修改hash-max-ziplist-value选项和hash-max-ziplist-entries选项。

集合对象(set)

集合对象的编码可以是intset和hashtable之一

  1. intset编码
    intset编码的集合对象底层实现是整数集合,所有元素都保存在整数集合中。
  2. hashtable编码
    hashtable编码的集合对象底层实现是字典,字典的每个键都是一个字符串对象,保存一个集合元素,不同的是字典的值都是NULL;可以参考java中的hashset结构。

集合对象编码转换

  • 集合对象使用intset编码需要满足两个条件:一是所有元素都是整数值;二是元素个数小于等于512个;不满足任意一条都将使用hashtable编码
  • 以上第二个条件可以在Redis配置文件中修改et-max-intset-entries选项。

有序集合对象(zset)

有序集合的编码可以是ziplist和skiplist之一

  1. zipList
    ziplist编码的有序集合对象底层实现是压缩链表,其结构与哈希对象类似,不同的是两个紧密相连的压缩列表节点,第一个保存元素的成员 ,第二个保存元素的分值,而且分值小的靠近表头,大的靠近表尾
  2. skiplist编码
    skiplist编码的有序集合对象底层实现是跳跃表和字典两种;
    每个跳跃表节点都保存一个集合元素,并按分值从大到小排列,节点的object属性保存了元素的成员,score属性保存分值
    字典的每个键值对保存一个集合元素,字典的键保存元素成员,字典的值保存分值

Redis的五大数据类型的底层实现_数据结构_02

为何skiplist编码要同时使用跳跃表和字典实现?

  • 跳跃表优点就是有序,但是查询分值复杂度为O(logn); 字典查询分值复杂度为O(1),但是无序,所以结合连个两个集合的优点进行实现
  • 虽然采用两个结构但是集合的元素成员和分值是共享的,两个结构通过指针指向同一指针,不会浪费内存

有序集合编码转换:

  1. 有序集合对象使用ziplist编码需要满足两个条件:一是所有元素长度小于64字节;二是元素个数小于128个;不满足任意一条件将使用skiplist编码
  2. 以上两个条件可以在Redis配置文件中修改zset-max-ziplist-entries选项和zset-max-ziplist-value选项。

ziplist的最大的好处就是相比skiplist节约大量内存,但是在插入、删除、查询等操作上的时间复杂度与skiplist都是无法比拟的,当保存的数据比较少时,操作的时间自然可以接受,内存就是关键因素。

ziplist在Redis中主要用于Hash Table、List、Sorted Set数据类型小范围数据的存储,本文描述的ziplist的存储都是无序的,如何实现在Sorted Set中的有序存储待以后分析,无序变有序无疑又增加的时间复杂度。

总之,ziplist就是一种用时间换空间的策略。

总结

在redis中五大对象中,string对象是唯一个可以被其他四种数据对象作为内嵌对象的

列表(list)、哈希(hash)、集合(set)、有序集合(zset)底层实现都用到了压缩列表结构,并且使用压缩列表结构的条件都是在元素个数比较少、字节长度较短的情况下

四种数据对象使用压缩列表的优点

  1. 节约内存,减少内存开销,Redis是内存型数据库,所以一定情况下减少内存开销是非常有必要的。
  2. 减少内存碎片,压缩列表的内存块是连续的,并分配内存的次数一次即可
  3. 压缩列表的新增、删除、查找操作的平均时间复杂度是O(N),在N再一定的范围内,这个时间几乎是可以忽略的,并且N的上限值是可以配置的。
  4. 四种数据对象都有两种编码结构,灵活性增加。

为什么 redis 实现有序集合不用红黑树或者平衡二叉树呢?

在实现方面,红黑树实现更加复杂,跳跃表实现比较简单,也更加直观,更加灵活。
跳表区间查找数据的效率更高一些。
红黑树/平衡二叉树这种树形结构,每次每隔两个节点建一个索引,而跳跃表可以多个节点,不限于两个节点。
跳跃表插入或删除操作只需要修改节点前后的指针,而不需要对多个节点都进行调整,而平衡二叉树则需要左旋或者右旋实现平衡。
从内存占用上来说,skiplist比平衡树更灵活一些。一般来说,平衡树每个节点包含2个指针(分别指向左右子树),而skiplist每个节点包含的指针数目平均为1/(1-p),具体取决于参数p的大小。如果像Redis里的实现一样,取p=1/4,那么平均每个节点包含1.33个指针,比平衡树更有优势。


标签:编码,对象,数据类型,ziplist,Redis,集合,内存,字符串,底层
From: https://blog.51cto.com/u_15850876/5804537

相关文章

  • Zookeeper - zookeeper 底层实现数据一致性
    zookeeper主要应用事务日志和数据快照来实现底层数据一致性;事务日志事务日志是记录zookeeper事务操作的日志文件。以ZXID为事务日志文件名的后缀,可以快速的定位到查询......
  • Redis启动命令
     启动redis 打开cmd窗口,执行命令:redis-serverredis.windows.conf......
  • redis缓冲区:缓冲区大小可以随意设置吗?
    前言我们都知道缓冲区是为了应对数据传递两端发送和接收速度不一致的方案。但如果缓冲区占用的资源超出设定的上限时,就会出现缓冲区溢出。Redis是典型的客户端-服务端架构,在......
  • RedisTimeSeries实时时序数据库
    一、时序数据库是什么?时间序列数据库TimeSeriesDatabase(TSDB)时序数据是随时间不断产生的一系列数据,简单来说,就是带时间戳的数据。1.时序数据库相关概念度量Me......
  • Redis基础课程讲义
    1.1什么是RedisRedis是一个基于内存的key-value结构数据库。Redis是互联网技术领域使用最为广泛的存储中间件,它是「RemoteDictionaryService」的首字母缩写,也就是「远......
  • js中的数据类型
    JS中有哪些数据类型开头JS中的数据类型可以分为基本数据类型和引用数据类型基本数据类型主要有:Number、String、Boolean、Null、Undefined、ES6新增的Symbol以及ES10新......
  • Redis缓存满了怎么办
    Redis缓存满了怎么办?​ 当使用redis作为数据库的缓存,是为了避免业务应用直接从后端数据库读取数据;采用redis储存数据可以提升系统的响应速度.​ 但是如果将大量数据全......
  • Redis持久化----RDB
    Redis持久化----RDB原理当采用RDB这种方式来持久化Redis中的数据时,是向RDB文件中写入redis的快照;什么是内存快照:​ 所谓内存快照,就是指内存中的数据在某⼀个时刻的......
  • 力扣(leetcode) 66. 加一(数据类型之间的转换)
    题目在这:​​https://leetcode-cn.com/problems/plus-one/​​题目分析:给了一个整数,但是这个整数的每一位存在一个数组里面,比如243这个数。给你的变成[2,4,3],让你把他加一......
  • Redis的单线程和高性能
    Redis是单线程吗?Redis的单线程主要是指Redis的网络IO和键值对读写是由一个线程来完成的,这也是Redis对外提供键值存储服务的主要流程。但Redis的其他功能,比如持久......