Redis(一):基本数据类型与底层存储结构
最近在整理有关redis的相关知识,对于redis的基本数据类型以及其底层的存储结构简要的进行汇总和备注(主要为面试用)
Redis对外提供的基本数据类型主要为五类,分别是
- STRING:可以存储字符串、数字
- LIST:列表,链表的每个节点存储一个字符串对象
- HASH:包含键值对的无需散列表
- SET:无序集合,集合中包含的是不重复的集合对象
- ZSET:有序集合,是有一对一对字符串成员-浮点数分值所构成的有序映射,排序规则由分值大小所决定
以上是我们在使用Redis的时候经常见到的五种数数据结构,这五种数据结构在底层存储上又有着千丝万缕的联系;例如字符串对象作为一个最基本的存储对象,其在上午五种数据结构中均有应用,那么具体在Redis底层数据结构中是如何构造这五种数据结构的,本文将对底层存储做深入解析。
文章中所描述的数据结构大都基于2.9版本,如有描述不对还请留言指正,万分感谢
一、STRING
字符串对象根据保存值的类型、长度不同,可以分为三种存储结构
- 如果存储的是整数值(可以用long表示),则底层通过如下结构进行存储,其中type代表当前对象为STRING对象,encoding表示当前对象的编码格式,ptr的属性保存是真实的值;

举例:
- 如果存储的是字符串且字符串长度超过39字节,则底层通过如下结构进行存储,其中type代表当前对象为STRING对象,encoding表示当前对象的编码格式,ptr为指针指向一个SDS(shshdr:简单动态字符串对象)来保存具体的值;

举例:
- 如果存储的是字符串且字符串长度未超过39字节,则底层通过如下结构进行存储(需要一块连续的内存空间),其中type代表当前对象为STRING对象,encoding表示当前对象的编码格式,ptr为指针指向一个SDS(shshdr:简单动态字符串对象)来保存具体的值;

举例:
- 存储结构差异
- embstr需要一块连续的内存空间,因此其效率上比raw方式要高
- emstr在内存分配以及内存释放时只需要一次接口,而raw方式需要两次(因为存在redisObject和shshdr两个对象)
- embstr为只读对象,任何对embstr编码对象的修改都会导致对象的编码格式变为raw
- int/embstr编码格式的字符串对象在满足一定条件后会自动转为raw编码格式
- 字符串对象常用的命令
| 命令 | 作用 | 备注 |
| set | 设置key的值 | 根据值不同底层会采用三种不同的编码进行存储 |
| get | 获取字符串对象值 | 对于int编码格式有个值拷贝-转换的过程 |
| append | 在现有字符串值后面追加新的值 | int/embstr编码格式对象会先转换为raw后在执行追加操作 |
| incrbyfloat | 对浮点型数值进行加法操作 | |
| incrby | 对整数型数值进行加法操作 | embstr/raw不能执行此命令 |
| decrby | 对整数型数值进行减法操作 | embstr/raw不能执行此命令 |
| strlen | 返回字符串长度 | int编码格式需要拷贝对象并转换为raw格式后在执行操作 |
| setrange | 在字符串指定索引上的值设置为给定值 | int/embstr需要转换为raw后在执行操作 |
| getrange | 返回字符串指定索引的值 | int编码格式需要拷贝对象并转换为raw格式后在执行操作 |
二、LIST
列表对象根据存储数据的长度以及存储数据元素个数的不同,可以分为两种存储结构:
- 如果列表对象保存的所有字符串对象值的长度均未超过64字节且列表对象保存元素数量小于512个的时候,就采用ziplist(压缩列表)格式存储

- 如果列表对象保存的所有字符串对象值的长度有超过64字节或者列表对象保存元素数量大于等于512个的时候,就采用linkedlist(双端链表)格式存储

- 存储结构差异
- 压缩列表是有一系列特殊编码的连续内存块组成的顺序型数据结构,而双端链表则不需要连续的内存块
- 压缩列表的每个节点是由三部分组成(previous_entry_length/encoding/content),其中previous_entry_length是记录前一个节点的长度,以便程序可以通过任意节点的指针计算出前一个节点的起始位置;而双端链表则必须通过头尾节点进行遍历获取
- 由于previous_entry_length属性会随着前一个节点的字节长度不同而存储1或者5字节,如果新增的头结点长度大于254字节,会导致当前头结点的previous_entry_length(假设当前节点的previous_entry_length为1)的长度无法保存新节点的长度,此时程序会对原头节点进行内存空间重新分配,最坏的情况是新增的头结点导致原列表中的所有元素全部重新分配;而双端链表则不会存在该问题;因此在使用列表对象时要考虑连锁更新的问题;
三、HASH
哈希对象根据存储键值对数据长度以及键值对数量,可以分为两种存储结构:
- 如果hash对象保存的所有键值对的字符串长度小于64直接且键值对数量小于512个,采用ziplist(压缩列表)编码存储

key-value总是以成对的方式存在,存储顺序类似于栈,先添加的键值对会存在列表的前面,后添加的会在列表的尾部;
- 如果hash对象保存的键值对的字符串长度有超过64字节或者键值对数量大于等于512个,将采用hashtable编码存储

- 上述存储格式会随着存储的内容变化进行编码格式转变,转变只能从ziplist转换为hashtable
四、SET
集合对象根据存储数据的长度以及存储数据元素类型的不同,可以分为两种存储结构:
- 如果列表对象保存的所有元素均是整数型且保存元素数量不超过512个的时候,就采用intset编码存储

- 如果列表对象保存的元素有不是整数型或者保存元素数量超过512个的时候,就采用hashtable编码存储

- 上述存储格式会随着存储的内容变化进行编码格式转变,转变只能从intset转换为hashtable
- 在实际使用过程中,需要提前规划好存储数据内容,尽量不要出现编码格式转换
五、ZSET
有序集合对象根据数据元素数量以元素成员长度,可以分为两种存储结构:
- 如果有序集合对象保存的元素数量小于128个且有序结合对象保存的元素成员的长度都小于64字节,就采用ziplist(压缩列表)编码存储

使用压缩列表的有序结合中每个对象由两个节点构成,第一个节点保存元素的具体值,第二个节点保存元素的分值;默认情况元素是按照分值从小到大排序;
- 如果有序集合对象保存的元素数量>=128个或有序结合对象保存的元素成员的长度存在>=64字节,就采用skplist(跳跃表【详细请参见:算法-跳跃表原理与实现】)和dict编码存储

为了兼顾查找与范围查找,有序集合需要同时使用跳跃表与字典
- 字典可以保证O(1)复杂度直接根据指定key查找成员值
- 跳跃表可以在O(logN)的复杂度下完成范围查找,远远优于字典的O(NlogN)
在底层存储上,针对相同的数据对象以及分值,跳跃表与字典会通过指针进行共享,因此不会产生较多的内存浪费
六、总结
Redis对外提供的是上述五种数据类型,但是在底层构造这五种数据类型时,底层实际上使用了包括“简单动态字符串”、“链表”、“字典”、“跳跃表”、“整数集合”、“压缩列表”来构造
根据存储数据的类型、长度以及数量,不同存储格式之间可以进行动态转换,有的存储结构体现是查询速度,有的存储接口体现的空间占用
STRING |
INT |
| embstr | |
| raw | |
| LIST | ziplist |
| linkedlist | |
| HASH | ziplist |
| ht | |
| SET | intset |
| ht | |
| ZSET | ziplist |
| skiplist |
Redis(一):基本数据类型与底层存储结构的更多相关文章
- Redis-基本数据类型与内部存储结构
1-概览 Redis是典型的Key-Value类型数据库,Key为字符类型,Value的类型常用的为五种类型:String.Hash .List . Set . Ordered Set 2- Redi ...
- Redis(三)--- Redis的五大数据类型的底层实现
1.简介 Redis的五大数据类型也称五大数据对象:前面介绍过6大数据结构,Redis并没有直接使用这些结构来实现键值对数据库,而是使用这些结构构建了一个对象系统redisObject:这个对象系统包 ...
- Redis常用数据类型及其存储结构(源码篇)
一.SDS 1,SDS源码解读 sds (Simple Dynamic String),Simple的意思是简单,Dynamic即动态,意味着其具有动态增加空间的能力,扩容不需要使用者关心.Strin ...
- Redis(一) 数据结构与底层存储 & 事务 & 持久化 & lua
参考文档:redis持久化:http://blog.csdn.net/freebird_lb/article/details/7778981 https://blog.csdn.net/jy69240 ...
- C++实现线性表的链接存储结构(单链表)
将线性表的抽象数据类型定义在链接存储结构下用C++的类实现,由于线性表的数据元素类型不确定,所以采用模板机制. 头文件linklist.h #pragma once #include <iost ...
- 数据的存储结构浅析LSM-Tree和B-tree
目录 顺序存储与哈希索引 SSTable和LSM tree B-Tree 存储结构的比对 小结 本篇主要讨论的是不同存储结构(主要是LSM-tree和B-tree),它们应对的不同场景,所采用的底层存 ...
- Redis之(二)数据类型及存储结构
Redis支持五中数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及zset(sortedset:有序集合). Redis定义了丰富的原语命令,可以直接与Redis ...
- Redis之数据存储结构
今天去中关村软件园面试,被问到:你做项目用到的Redis处理数据用的什么结构?顿时石化,”用到的结构,不就是key-value嘛,还有什么结构?“.面试官说:“平时除了工作,要加强学习,下面的面试我觉 ...
- Redis数据存储结构之String
前言: 在Redis使用中,我们最常使用的操作是set key value,或 get key value .这里面包含了redis最基本的数据类型:String,字符串类型是redis中最基本的类型 ...
随机推荐
- Pandas中Series与Dataframe的区别
1. Series Series通俗来讲就是一维数组,索引(index)为每个元素的下标,值(value)为下标对应的值 例如: arr = ['Tom', 'Nancy', 'Jack', 'Ton ...
- Java读写文件常用方法
一.字符流:读写纯文本(txt,csv等), 1 字符流写文件主要用:FileWriter,BufferedWriter,PrintWriter 1.1 测试 FileWriter 写入 privat ...
- SQL——with as 临时表
一.WITH AS的含义 WITH AS短语,也叫做子查询部分(subquery factoring),可以让你做很多事情,定义一个SQL片断,该SQL片断会被整个SQL语句所用到.有的时候,是 ...
- torch.utils.data.DataLoader与迭代器转换
在做实验时,我们常常会使用用开源的数据集进行测试.而Pytorch中内置了许多数据集,这些数据集我们常常使用DataLoader类进行加载. 如下面这个我们使用DataLoader类加载torch.v ...
- Acwing_蓝桥_递归
一.关于由数据范围反推算法复杂度及其算法 关于输入输出:问题规模小于105:cin,scanf都差不多,但是要是大于105推荐使用scanf和printf. 二.关于递归 1.定义 自己调用自己 2. ...
- 用 JuiceFS 备份 Nginx 日志可以这么简单
在我们线上的生产环境中要备份的东西很多,各种服务日志.数据库数据.用户上传数据.代码等等.用 JuiceFS 来备份可以节省你大量时间,我们会围绕这个主题写一系列的教程,整理出一套最佳实践,方便大家. ...
- Web渗透测试入门之SQL注入(上篇)
题记: 本来今天想把白天刷的攻防世界Web进阶的做个总结,结果估计服务器抽疯环境老报错,然后想了下今天用到了SQL注入,文件上传等等,写写心得.相信很多朋友都一直想学好渗透,然后看到各种入门视频,入门 ...
- HMS Core在MWC2022展示最新开放能力,助力开发者构建精品应用
[2022年2月28日,巴塞罗那]世界移动通信大会MWC2022在巴塞罗那开幕.HMS Core设立了3个展台(Fira Gran Via,Hall 1),向全球开发者展示HMS Core 6的全新开 ...
- windows痕迹清理的基本思路和思考逻辑
1.痕迹清理的基本概念 在渗透测试的过程结束后清理自己在从开始接触到目标计算机是开始所有操作的痕迹 2.痕迹清理的目的 为下一步的渗透测试拖延时间 提高隐蔽性 所有的痕迹清理都不是绝对的,只要和计算机 ...
- [题解]第十一届北航程序设计竞赛预赛——F.序列
题目描述 (1,--,n)的一个排列S,定义其对应的权值F[S]为:将S划分为若干段连续子序列,每个子序列都是上升序列,F[S]的值等于能划分出的最小段数. 求n的全排列的F[S]的和,答案mod(1 ...