- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Redis底层数据结构之dict、ziplist、quicklist详解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
此前我们学习了常见的reids数据类型,这些数据类型都需要底层的数据结构的支持,现在我们来看看redis常见的底层数据结构:dict、ziplist、quicklist.
dict就是“字典”的意思,它是redis中的一种使用的非常多的底层的数据结构,除了hash会使用字段之外,整个 redis 数据库的所有 key 和 value 也组成了一个全局字典dict,还有带过期时间的 key 也是一个字典expires(存储在 redisdb 数据结构中).
redis 中的字典相当于 jdk1.7中的 hashmap,内部实现也差不多类似,都是通过 “数组 + 链表” 的链地址法来解决部分哈希冲突,同时这样的结构也吸收了两种不同数据结构的优点.
和jdk1.7中的hashmap不同的是,reids的字典结构内部包含两个hashtable变量,通常情况下只有一个hashtable有值,另一个为空表,但是在字典扩容缩容时,需要分配新的hashtable,并且使用了一种叫做渐进式搬迁(rehash)的机制来提高dict的缩放效率,这时候两个hashtable分别存储旧的和新的 hashtable,待搬迁结束后,旧的将被删除,新的 hashtable 取而代之.
正常情况下,当 hash 表中元素的个数等于数组的长度时,就会开始扩容,扩容的新数组是原数组大小的2倍。不过如果 redis 正在做 bgsave或者bgrewriteaof(持久化),为了减少内存占用,redis 尽量不去扩容,但是如果 hash 表非常满了,达到了数组长度的 5 倍了,这个时候就会强制扩容.
当 hash 表因为元素逐渐被删除变得越来越稀疏时,redis 会对 hash 表进行缩容来减少hash表的第一维数组空间占用。所用的条件是:元素个数低于数组长度的10%,缩容不会考虑 redis 是否在做 bgsave.
在扩容和收缩的时候,如果哈希字典中有很多元素,一次性将这些键从ht0(正在使用的hashtable)全部rehash到ht1(另一个空的hashtable)的话,由于redis是单线程模型,那么可能会导致服务器在一段时间内停止服务。所以,采用渐进式rehash的方式,数据的迁移并不是一步完成的,因此dict内部还有一个rehashidx属性用来控制rehash,默认置为-1.
渐进式rehash采用的是一种分而治之的方式,它以bucket(桶)为基本单位进行渐进式的数据迁移,每步完成一个bucket的迁移,直至所有数据迁移完毕。这种方式将rehash的操作分摊在每一个的访问中,避免集中式rehash而带来的庞大计算量.
在渐进式rehash的过程中,如果有删除、修改、查询操作,则会在两个哈希表ht[0]和ht[1]上进行,先操作ht[0],如果没找到,再操作ht[1],则新增的数据则会被保存在ht[1]中,ht[0]中包含的键值对数量会只减不增,并随着 rehash 操作的执行而最终变成空表.
ziplist又名压缩列表,见其名知其意,这种数据结构就比较节省内存空间,redis中对于list(3.2版本之前)、hash、zset类型的数据,如果元素较少,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么redis就会使用压缩列表来存储.
ziplist本身可以有序也可以无序。当作为list(3.2版本之前)和hash的底层实现时,节点之间没有顺序;当作为zset的底层实现时,节点之间会按照score大小顺序排列,元素和score被分别存储为两个节点,元素在前,score在后.
ziplist的节省空间是相较于数组而言的,ziplist和数组一样同样采用一整块连续的空间来存储数据,数组在实际存储时,每一个元素所占的实际大小是一样的,并且是以最大的元素的大小为准,这样就能进行快速的根据索引访问,而ziplist则允许每一个entry节点(对于数组中的元素)所占的实际大小不同,另外,节点之间没有存储前驱和后继的指针,以此节约空间.
ziplist支持正序或者倒序的遍历,往ziplist里插入一个entry 时间复杂度 平均:o(n),最坏:o(n²),从ziplist里删除一个entry 时间复杂度 平均:o(n), 最坏:o(n²)。最坏为o(n²)是因为redis连锁更新导致的,但这种情况出现的概率不高.
ziplist和数组类似,删除和添加节点都可能涉及到其他节点位置的移动,因此只适用于元素数据量较少,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串的情况.
redis的ziplist采用一系列特殊编码的连续内存块,一个压缩列表出了在头部包含一些整体信息之外,剩下的部分可以包含任意多个节点(entry).
整体结构如下:
ziplist的entry用于存储正数或者字符串(字节数组),且每个节点的长度都是不一样的,因此节点的长度只能由这个节点自己来保存,redis的ziplist中,entry由三部分构成:
previous_entry_length用于记录上一个节点的长度(字节),为了方便反向遍历ziplist,其本身的长度可能是8bit或者40bit.
content表示当前节点的值,可以是数字或字符串(字节数组),encoding 表示当前节点数据的编码规则,即data属性的数据类型以及长度,其中encoding其中高2位用来区分整数节点和字符串节点(高2位为11时是整数节点),根据值的类型不同,一共有九种编码类型.
字节数组的encoding类型3种,encoding有8位、16位、40位三种长度,context的长度也是变化的:
。
encoding长度 | encoding格式 | content格式 |
---|---|---|
8bit | 高两位固定00,后6位表示字节数组的长度 | 长度小于等于63(2^6-1)字节的字节数组 |
16bit | 高两位固定01,后14位表示字节数组的长度。 | 长度小于等于16383(2^14-1)字节的字节数组 |
40bit | 高两位固定10,后38位表示字节数组的长度。 | 长度小于等于4294967295(2^32-1)字节的字节数组 |
。
整数值的encoding类型6种,固定长度8位,高两位固定11,表示整数,因此需要后两位来表示不同的整数类型。整数类型的content的长度虽然也是可变的,但固定范围内的整数长度一样,也就是说content长度只有固定的几种.
encoding长度 | encoding格式 | content格式 |
1bit | 1111xxxx,后四位用来表示content。 | 4bit长的无符号整数,即介于0至12之间,没有content字段,在encoding中存储。 |
11111110 | 8bit,有符号整数。 | |
11000000 | 16bit,有符号整数。 | |
11110000 | 24bit,有符号整数。 | |
11010000 | 32bit,有符号整数。 | |
11100000 | 64bit,有符号整数。 |
redis 的list在3.2版本之前在元素较少时实际上是采用ziplist结构,当链表entry数据超过512、或单个value 长度超过64,底层就会转化成linkedlist编码,而redis3.2及其之后则采用quicklist结构代替ziplist和linkedlist.
ziplist存储在一段连续的内存上,所以存储和查找效率很高,顺序io访问。但是,它不利于修改操作,插入和删除操作需要频繁的申请和释放内存。特别是当ziplist长度很长的时候,一次realloc可能会导致大批量的数据拷贝,适用于存储整数和短字符串.
双向链表linkedlist便于在表的两端进行push和pop操作,在插入节点上复杂度很低,但是它的内存开销比较大。首先,它在每个节点上除了要保存数据之外,还要额外保存两个prev 和 next指针(64 位操作系统占用 8 个字节);其次,双向链表的各个节点是单独的内存块,地址不连续,随机io访问,节点多了容易产生内存碎片,影响内存管理效率.
quicklist将 linkedlist 按段切分,每一段使用 ziplist 来紧凑存储,多个 ziplist 之间使用双向指针串接起来.
首先quicklist就是一个标准的双向链表的配置,有head 和tail节点,每个节点是一个quicklistnode节点,包含prev和next指针,内部还包含一个ziplist,使用ziplist来保存数据,而ziplist实际上含有多个entry节点,保存着数据。相当与一个quicklistnode节点保存的是一片数据,而不再是一个数据.
quicklist将二者的优点结合起来,在宏观上是一个双向链表结构,插入、删除quicklistnode节点的成本很低,不需要移动其他节点的位置,而在微观上,每一个quicklistnode节点又是一个个的ziplist,内部包含多个数据,ziplist内存连续,减少了内存碎片,同时由于每一个ziplist不是很大(大小可以配置),因此插入和删除操作不会进行大量的数据移动.
相关文章:
https://redis.io/topics/data-types 。
https://redis.io/topics/data-types-intro 。
到此这篇关于redis的底层数据结构之dict、ziplist、quicklist详解的文章就介绍到这了,更多相关redis底层数据结构内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://blog.csdn.net/weixin_43767015/article/details/120411000 。
最后此篇关于Redis底层数据结构之dict、ziplist、quicklist详解的文章就讲到这里了,如果你想了解更多关于Redis底层数据结构之dict、ziplist、quicklist详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在运行一个 redis 实例,我在其中存储了很多具有整数字段和值的哈希值。具体来说,有很多形式的散列 {1: , 2: , ..., ~10000: } 我最初使用 hash-max-ziplis
基地提供ZipList ,它只是 [] 的包装器在哪里 基于 zip而不是笛卡尔积。这不是默认值,因为它与 Monad [] 不一致。例如,但有些人觉得它更直观,而且这两种行为在不同的情况下都很有用。
我如何编译和执行 Redis ziplist.c作为单独的可执行文件? 我只想执行 ziplist.c 主函数。 最佳答案 您可以使用以下命令: gcc -std=c99 -O2 -DZIPLIST_
此前我们学习了常见的reids数据类型,这些数据类型都需要底层的数据结构的支持,现在我们来看看redis常见的底层数据结构:dict、ziplist、quicklist。 1 redis di
GHC Prelude 中列表的默认 monoid 是串联。 [1,2,3] <> [4,5,6]变成 [1,2,3] ++ [4,5,6]因此[1,2,3,4,5,6] 我想编写一个行为如下的 Zi
我想为我的自定义列表实现一个 Applicative 实例。 import Test.QuickCheck import Test.QuickCheck.Checkers import Test.Qu
这来自Haskell from First Principles一书中的练习。本练习是为 ZipList' 实现 Applicative,这类似于 Prelude 的 ZipList。书上有这个提示
假设我有一个数字列表和函数列表: val xs: List[Int] = List(1, 2, 3) val fs: List[Int => Int] = List(f1, f2, f3) 现在我想使
Typeclassopedia提出这个问题: Determine the correct definition of pure for the ZipList instance of Applicat
我正在从learn-you-a-haskell 书中学习Haskell 中的应用仿函数。 但是每当我尝试在 ghci 中输入以下代码时: :{ instance Applicative ZipList
应用程序的两个著名示例是 monad 和 ziplist。还有其他例子吗? 最佳答案 来自 Time flies like an applicative functor通过康纳麦克布莱德: Struc
我目前正在学习 Haskell 中的应用程序。如果我没记错的话,列表有两个不同的 Applicative 实例(List 和 ZipList - 第二个被定义为包装列表值的新类型)。 ZipList
我的问题示例:HMSET myhash field1 value1 field2 value2 而myhash只有这两个字段。 主要问题是如何计算hash-max-ziplist-value,以便我的
我是一名优秀的程序员,十分优秀!