专业术语

sds:simple dynamic string 简单动态字符串,redis自己开发的一个字符串的抽象类型

embstr:embedded sds string embstr编码的SDS,与SDS区别在于内存仅需要申请一次,而SDS需要申请两次。适用于短字符串,优点是效率高

Redis 对象结构

Redis 五种对象类型

redis对象数据结构如图所示:

每一个redis对象都用一个key进行存储,在redis对象中有对象类型和编码,对象类型和编码的对应关系如下:

string 字符串类型

应用场景:
5中类型中最简单、常用用的类型,扩展性是非常高得

  1. key是string,value可以是json,常规key-value缓存
  2. 可以用于一些计数器,比如文章点赞数

编码选择:
REDIS_ENCODING_INT:如果value是long类型的整数,则使用此编码
REDIS_ENCODING_RAW:专门保存长字符串的编码,如果value长度>=40(redis-3.3),则使用的是raw编码进行存储。

  • 不足:raw需要调用两次的内存分配函数来分别创建redisObjectsdshdr

REDIS_ENCODING_EMBSTR:专门保存短字符串的编码,如果value长度<40,则使用embstr编码,属于紧凑型的数据结构

  • 优点只需要一次内存分配,数据比较小的时候使用的是这种编码,数据更紧凑,效率更高
  • 缺点:没有提供修改的函数,所以是只读的,如果要对此编码数据进行修改,会变成raw再执行修改,然后结束

底层数据结构:

string的int编码:


在ptr指针中直接指向long类型整数

string的raw编码


ptr指向一个redis自定义的string类型->sdshdr,然后在buf缓冲中指向一个字符数组

string的embstr编码


于raw类似,只不过在所有属性都和RedisObject在一块连续的内存空间上,所以它只需要进行一次的内存申请,也代表着它很快!

string 相关命令

命令行 含义
set key value 赋值key的值为value
get key 获取key的value值
del key 删除key
expire key seconds 设置key在seconds秒后过期
setex key seconds value 赋值key的值为value,在seconds秒后过期
ttl key 查看key还有多久过期
setnx key value 如果key不存在,才新增key和value
strlen key 计算指定key的值的长度
incr key 加1
incrby key numbers 指定增加值,numbers可以是负值
mset key1 value1 key2 value2 … 批量添加
mget key1 key2 key3 … 批量获取

list 列表类型

应用场景:

  1. 各种列表, twitter的关注列表、粉丝列表等,最新消息排行
  2. 消息队列,可以利用Lists的PUSH操作,将任务存在Lists中,然后工作线程再用POP操作将任务取出执行。

编码选择:
当列表对象同时满足以下两个条件时,列表对象使用ziplist进行存储,否则使用linkedlist存储

  1. 列表对象保存的所有字符串元素的长度小于64字节
  2. 列表对象保存的元素数量小于512个

list的zpilist编码


ptr指向一个压缩列表,所有的压缩列表都类似数据结构

list的linkedlist编码


双向无循环的链表,无循环代表头和尾都指向NULL,不会形成一个循环,优点和java的链表一样,插入快但索引慢

list 相关命令

命令行 含义
lpush key value1 value2 左侧插入value
rpush key value1 value2 右侧插入value
lpop key 左侧弹出value
rpop key 右侧弹出value
llen key 查看key的长度
lindex key index 查看列表中某个index对应的value值
setnx key value 如果key不存在,才新增key和value
lrange key startIndex endIndex 查看指定元素,下标从0开始,-1为倒数第一个
ltrim key startIndex endIndex 让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除,下标同上

set 集合类型

应用场景:

  1. 增加的时候可以保证去重,比如文章中用户每次点赞的时候不允许重复

编码选择:
同时符合以下两种情况时,使用的是intset编码,否则采用hashtable编码

  1. 集合对象保存的所有元素都是整数值
  2. 集合对象保存的所有元素<=512个

set的intset编码(intset底层为整形集合实现)


intset编码与raw类似,只不过将字符数组换成了整形数组

set的hashtable编码(底层是字典,每个键都是字符串对象。字典对应的值为NULL)


set集合类型下使用了hashtable,那么它指向的value保存什么呢?
看图中可以看出,所有的数据都指向了NULL

set 相关命令

命令行 含义
sadd key value1 value2 添加元素到集合中
smembers key 查看集合中的所有元素
sismember key value 查看value是否在集合中
scard key 查询集合的长度
spop key 取出集合中的一个元素
del key 删除集合

zset 有序集合类型

应用场景:
一些需要对存放的数据进行排序,比如正序、倒序;不允许重复的成员,后面的数据会覆盖前面的数据

  1. 根据时间排序的新闻列表
  2. csdn的访问量排行榜

编码选择:
当zset满足以下两个条件的时候,使用ziplist,否则使用skiplist

  1. 保存的元素个数小于128个
  2. 保存的所有元素大小都小于64字节

zset的ziplist编码


一样使用ziplist压缩列表,在中间数据的部分保存的时key-value,连续存储的结构

zset的skiplist编码

typedef struct zset{// 跳表zskiplist *zsl;// 字典dict *dice;
} zset;


使用字典+跳表的方式,将字典和跳表两种数据结构组合
其中跳表通过在每个节点中(基于层和跨度等)维持多个指向其它节点的指针来实现快速访问
更多参考:Redis 源码分析(七) :skiplist

zset有序集合单独使用字典或跳表中的一种数据结构就可以实现,那么为什么要使用两者的组合呢?
  • 字典的优势在于如果要查找成员的分值,时间复杂度是O(1),但是字典是以无序的方式保存集合元素,所以每次范围操作的时候需要进行排序
  • 跳表的优势在于如果要执行范围操作,不需要进行排序,可以直接获取,但是跳表如果是进行查找操作,则时间复杂度为O(logN)
  • 因此Redis使用两种数据结构的组合,扬长避短的方式来共同实现有序集合
zset有序集合使用两种数据结构会不会造成数据冗余?

不会,两种数据结构会通过指针来共享相同数据元素的成员和分值,所以不会产生重复的成员和分值而造成内存的浪费。

zset 相关命令

命令行 含义
zadd key value1 score1 value2 score2 添加元素到有序集合中
zscore key value 查看key的score值,输出score>=负无穷,score<=正无穷的所有元素
zrange key 0 -1 正序输出
zrangebyscore key -inf +inf 正序输出
zrevrange key 0 -1 倒序输出
zcard key 查看key中的元素个数
zrangebyscore key indexStart endStart 获得key中score>=indexStart 且score<=endStart的元素,正序排列
zrevrangebyscore key indexStart endStart 同上,倒序排列
zrem key value 删除key中的元素value

hash 字典类型

应用场景:

  1. 存放结构化数据,比如user对象,修改时不需要反序列化可以直接修改

编码选择:
同时满足以下两种情况,则是zpilist,否则就是hashtable
3. hash里面的键值对中,key和value的长度全都小于46个字节
4. hash里面的键值对中,个数小于512个

hash的ziplist编码


zpilist压缩链表,数据部分存储的是一个对象的多个属性名和属性值,修改就是直接对所在数据位置修改,而不需要进行反序列化,因为它们是结构化存储,而不是像json一样当作一个string来存储

hash的hashtable编码


使用字典的方式存储属性名和属性值,修改也是直接对数据进行修改,不需要反序列化操作

hash 相关命令

命令行 含义
hset key name value 添加属性元素name和value到key中
hget key name 查看key的name值
hmset key name1 value1 name2 value2 批量添加key的属性元素
hmget key name1 name2 批量获取key的属性元素
hlen key 获得key的属性元素个数
hgetall key 查询key中的所有元素

思考

为什么redis要自己开发一个字符串类型?

主要有如下几种原因:

  1. C中字符串中没有长度数据,每次都需要进行计算,使用一个元素去遍历,直到\0结束符号,复杂度为O(n)
    而在SDS中的复杂度是O(1),更快
  2. 缓冲区溢出:C字符串是存在缓存冲溢出的情况,前提是在增加元素之前没有进行足够的内存分配
    而SDS动态的执行空间的扩展,API会自动的进行空间的扩展,
  3. 在C字符串中,内存的重新分配是很耗费性能的
    • 空间预分配:

      • 如果对SDS进行修改后,SDS的长度<1M,那么这个时候,预分配的len=free
      • 如果对SDS进行修改后,SDS的长度>=1M,只会按照1M去预分配
    • 惰性释放:
      • 在删除字符串的时候,不及时释放,还继续保留空间,下次使用的时候就不需要再进行申请了
  4. C字符串中字符串是以\0结尾的,并且字符串不能包含空字符串,因此C字符串使用范围较小
    而在SDS中,使用len来判断数据长度,可以保存任意的数据。

总结

文章以图解的方式介绍了Redis中的五种数据类型和8中数据编码,并对其应用场景进行分析举例,相信大家看完对Redis的底层有了一定的了解

8种编码encoding对比总结:

数据编码 描述 优点 缺点
REDIS_ENCODING_INT long类型的整数 占用内存少、速度快 仅仅针对long类型整数
REDIS_ENCODING_EMBSTR embstr编码的简单动态字符串 一次内存申请与释放 不适用于长的字符串
REDIS_ENCODING_RAW 简单动态字符串 适用于长的字符串存储 相对于embstr,需要两次内存申请与释放
REDIS_ENCODING_HT 字典 读写时间复杂度O(1) 占用内存较多
REDIS_ENCODING_LINKEDLIST 双向链表 元素的个数较多时,访问元素的时间比压缩列表更快 每个节点都维护了前置指针、后置指针,占用更多内存且内存不连续容易产生内存碎片
REDIS_ENCODING_ZIPLIST 压缩列表 空间连续,占用内存少,速度快 元素数量大时,访问元素的时间较长
REDIS_ENCODING_INTSET 整数集合 占用内存远小于hashtable 仅仅存整数
REDIS_ENCODING_SKIPLIST 跳表+字典 不满足ziplist可以使用skiplist来作为内部实现

Redis中数据结构和编码详细图解(应用场景及优缺点)相关推荐

  1. redis集群搭建二,详细图解说明

    前言:当你查看这篇文章的时候,相信你有了一定的linux基础知识,如果没有的话,建议先去学习练习,然后再来观看,要不然,看这篇文章,简直是云里雾里,天方夜谭! 本文版权归作者所有,但未经作者同意必须保 ...

  2. 六、redis中AKF问题解决方案

    导读 前面文章[一.深入理解redis之需要掌握的知识点 ]中,我们对redis需要学习的内容框架进行了一个梳理. [二.redis中String和List两种数据类型和应用场景 ].[二.redis ...

  3. key redis 遍历_解惑:Redis的HSCAN命令中COUNT参数的quot;失效quot;场景

    前提 ❝ 这是一篇Redis命令使用不当的踩坑经历分享 ❞ 笔者最近在做一个项目时候使用Redis存放客户端展示的订单列表,列表需要进行分页.由于笔者先前对Redis的各种数据类型的使用场景并不是十分 ...

  4. Redis ZSet数据结构实现排行榜功能

    Redis ZSet数据结构实现排行榜功能 一. 使用场景 公司新项目要求, 实现每日排行榜以及各省排行榜 二. 功能实现(Java) 1. 排行榜数据插入及更新 /*** @date: 2022/1 ...

  5. 图解Redis中的9种数据结构(高级面试,必备)

    如图所示,Redis中提供了9种不同的数据操作类型,他们分别代表了不同的数据存储结构. 图2-17 数据类型 String类型 String类型是Redis用的较多的一个基本类型,也是最简单的一种类型 ...

  6. Redis中五大数据结构的底层实现

    来自:DBAplus社群 作者介绍 田兆壮,新炬网络开发工程师.具备扎实的Java.Scala开发经验,熟练使用Python和Shell等脚本语言:具备前后端开发能力,熟练使用关系型数据库和非关系型数 ...

  7. 一文带你深入理解Redis中的底层数据结构,再也不怕不懂数据类型的底层了

    数据结构前言 都说Redis快,因为什么呢?只是因为它是内存数据库,所有操作都是基于内存进行的吗?其实不然,这与它的数据结构也是密不可分的.下面我们就来了解一下Redis的数据结构. Redis 数据 ...

  8. java必学之Redis中的数据结构

    目录 一.String不在适用Redis底层数据结构 1.String类型的内存消耗问题 2:String类型怎么保存数据 2.1RedisObject结构体: 2.2int.embstr和raw这三 ...

  9. 图解|深入理解跳表及其在Redis中的应用

    跳跃链表及其应用是非常热门的问题,深入了解其中奥秘大有裨益,不吹了,快开始品尝这美味的知识吧! 跳跃链表的基本概念 初识跳表 跳跃列表是一种数据结构.它允许快速查询一个有序连续元素的数据链表.跳跃列表 ...

最新文章

  1. 大数据处理——Hadoop解析(一)
  2. sap 分割评估_SAP那些事-实战篇-73-受托加工的几种方案探讨
  3. 数据库oracle 别名不能更新,数据库oracle改成mysql后Hibernate不能使用别名问题
  4. python统计字典中元素个数_Python计数器 | collections.Counter
  5. app漏洞扫描原理_绿盟--漏洞扫描系统NSFOCUS RSAS-S-v5.0
  6. 计算机投影到数字电视的方法,PC电脑投屏电视方法有几种【乐播投屏】
  7. lr常见问题汇总(持续更新版)
  8. Mac系统go版本升级
  9. android wear自动断开,如果你的手表总是自动断开与手机连接的话,可以看下。
  10. 置信区间用计算机如何计算,置信区间计算器
  11. 激光雷达发射的激光属于哪个范围???和毫米波波长相比如何???
  12. R语言使用oneway.test函数执行单因素方差分析(One-Way ANOVA)、使用aov函数执行单因素方差分析(aov函数默认组间方差相同)
  13. 【学习笔记】特殊数论函数求和
  14. Springboot集成百度地图实现定位打卡功能
  15. 二分图(染色法+匈牙利法)学习笔记
  16. 实战分析!三面蚂蚁核心金融部,稳进大厂
  17. Docker Study Notes_Design in 2023
  18. 一次由图片引起的致命崩溃问题
  19. sendfile详解
  20. AI人工智能学习准备之路线图

热门文章

  1. 网络编程之 osi七层协议
  2. 09-OSPF故障排查总结
  3. System Center 2016 Data Protection Manager 部署手册
  4. 《中国人工智能学会通讯》——11.64 基于成对约束的属性特征选择
  5. 网络事件触发自己主动登录
  6. HP5200打印机从控制面板手动配置TCP/IP 参数
  7. 彻底崩溃,一个电脑白痴和黑客的对话!
  8. 根治“Microsoft JET Database Engine 错误 80004005”
  9. 0057-简单的累加
  10. 《UNIX网络编程 卷2:进程间通信(第2版)》——1.4 名字空间