详解Redis中的数据结构


详解Redis中的数据结构

在实际开发,Redis使用会频繁,那么在使用过程中我们该如何正确抉择数据类型呢?哪些场景下适用哪些数据类型。而且在面试中也很常会被面试官问到Redis数据结构方面的问题:

  • Redis为什么快呢?
  • 为什么查询操作会变慢了?
  • Redis Hash rehash过程
  • 为什么使用哈希表作为Redis的索引

当我们分析理解了Redis数据结构,可以为了我们在使用Redis的时候,正确抉择数据类型使用,提升系统性能。【相关推荐:Redis视频教程】

Redis底层数据结构

Redis 是一个内存键值Redis 数据库,且键值对数据保存在内存中,因此key-value基于内存的数据操作,其效率高,速度快;

其中,RedisKey类型,String 支持的 Redis 类型包括了 valueStringListHashSetSorted Set等。BitMap 能够之所以能够广泛地适用众多的业务场景,基于其多样化类型的Redis

valueRedis的数据类型是基于为Value自定义的对象系统Redis实现的,

typedef struct redisObject{
    //类型
    unsigned type:4;
    //编码
    unsigned encoding:4;
    //指向底层实现数据结构的指针
    void *ptr;
    ….. 
}

redisObject除了记录实际数据,还需要额外的内存空间记录数据长度、空间使用等元数据信息,其中包含了 8 字节的元数据和一个 8 字节指针,指针指向具体数据类型的实际数据所在位置:

image.png

其中,指针指向的就是基于redisObject的底层数据结构存储数据的位置,Redis的底层数据结构:Redis,双向链表、跳表,哈希表,压缩列表、整数集合实现的。

那么Redis底层数据结构是怎么实现的呢?

Redis底层数据结构实现

我们先来看看SDS比较简单的Redis,双向链表,整数集合

SDS、双向链表和整数集合

SDS,使用SDS字段记录已使用的字节数,将获取字符串长度复杂度降低为O(1),而且len惰性释放空间的,你SDS了空间,系统把数据记录下来下次想用时候可直接使用。不用新申请空间。
image.png
整数集合,在内存中分配一块地址连续的空间,数据元素会挨着存放,不需要额外指针带来空间开销,其特点为内存紧凑节省内存空间,查询复杂度为O(1)效率高,其他操作复杂度为O(N);

双向链表, 在内存上可以为非连续、非顺序空间,通过额外的指针开销前驱/后驱指针串联元素之间的顺序。

其特点为节插入/更新数据复杂度为O(1)效率高,查询复杂度为O(N);

free哈希表

哈希表,其实类似是一个数组,数组的每个元素称为一个哈希桶,每个哈希桶中保存了键值对数据,且哈希桶中的元素使用Hash结构,
image.png

因此,哈希桶元素保存的并不是键值对值本身,而是指向具体值的指针,所以在保存每个键值对的时候会额外空间开销,至少有增加24个字节,特别是dictEntryValue的键值对,每一个键值对就需要额外开销24个字节空间。当保存数据小,额外开销比数据还大时,这时为了节省空间,考虑换数据结构。

那来看看全局哈希表全图:
image.png
虽然哈希表操作很快,但String数据变大后,就会出现一个潜在的风险:哈希表的冲突问题和 Redis开销问题这可以解释为什么哈希表操作变慢了?

当往哈希表中写入更多数据时,哈希冲突是不可避免的问题 , Redis 解决哈希冲突的方式,就是链式哈希,同一个哈希桶中的多个元素用一个链表来保存,它们之间依次用指针连接,如图所示:
image.png

当哈希冲突也会越来越多,这就会导致某些哈希冲突链过长,进而导致这个链上的元素查找耗时长,效率降低。

为了解决哈希冲突带了的链过长的问题,进行rehash操作,增加现有的哈希桶数量,分散单桶元素数量。那么rehash过程怎么样执行的呢?

rehash

为了使Rehash 操作更高效,使用两个全局哈希表:哈希表 1 和哈希表 2,具体如下:

  • 将哈希表 2 分配更大的空间,
  • 把哈希表 1 中的数据重新映射并拷贝到哈希表 2 中;
  • 释放哈希表 1 的空间

但由于表1和表2在重新映射复制时数据大,如果一次性把哈希表 1 中的数据都迁移完,会造成 rehash 线程阻塞,无法服务其他请求。

为了避免这个问题,保证Rediss能正常处理客户端请求,Redi采用了渐进式Redis

每处理一个请求时,从哈希表 1 中依次将索引位置上的所有 entries 拷贝到哈希表 2 中,把一次性大量拷贝的开销,分摊到了多次处理请求的过程中,避免了耗时操作,保证了数据的快速访问。

1.jpg

在理解完 rehash哈希表相关知识点后,看看不常见的压缩列表和跳表。

压缩列表与跳表

压缩列表,在数组基础上,在压缩列表在表头有三个字段 zlbytes、zltail 和 zllen,分别表示列表长度、列表尾的偏移量和列表中的 entry 个数;压缩列表在表尾还有一个 zlend,表示列表结束。
image.png

优点:内存紧凑节省内存空间,内存中分配一块地址连续的空间,数据元素会挨着存放,不需要额外指针带来空间开销;查找定位第一个元素和最后一个元素,可以通过表头三个字段的长度直接定位,复杂度是 O(1)。

跳表 ,在链表的基础上,增加了多级索引,通过索引位置的几个跳转,实现数据的快速定位,如下图所示:

比如查询33

2.jpg

特点:当数据量很大时,跳表的查找复杂度为O(logN)。

凡人网络购物系统jsp版(JspShop) 凡人网络购物系统jsp版(JspShop)

基于jsp+j*abean+access(mysql)三层结构的动态购物网站,v1.2包含v1.0中未公开的数据库连接 的j*a源文件 一,网站前台功能: 产品二级分类展示:一级分类--二级分类--产品列表--详细介绍(名称,图片,市场价,会员价,是否推荐,功能介绍等) 产品搜索:关键字模糊搜索 定购产品:选择商品--确认定购--填写收货人信息--选择付款方式--订单号自动生成(限登录用户)

凡人网络购物系统jsp版(JspShop) 0 查看详情 凡人网络购物系统jsp版(JspShop)

综上所述,可以得知底层数据结构的时间复杂度:

数据结构类型 时间复杂度
哈希表 O(1)
整数数组 O(N)
双向链表 O(N)
压缩列表 O(N)
跳表 O(logN)

Hash自定义的对象系统类型即为RedisRedis的数据类型,Value的数据类型是基于底层数据结构实现的,那数据类型有哪些呢?

Redis数据类型

RedisStringListHashSorted Set比较常见的类型,其与底层数据结构对应关系如下:

数据类型 数据结构
String SDS(简单动态字符串)
List 双向链表
压缩列表
Hash 压缩列表
哈希表
Sorted Set 压缩列表
跳表
Set 哈希表
整数数组

数据类型对应特点跟其实现的底层数据结构差不多,性质也是一样的,且

Set,基于SDS实现,适用于简单String存储、key-value实现分布式锁、计数器(原子性)、分布式全局唯一ID。

setnx key value, 按照元素进入List的顺序进行排序的,遵循FIFO(先进先出)规则,一般使用在 排序统计以及简单的消息队列。

List , 是字符串Hash和字符串key之间的映射,十分适合用来表示一个对象信息 ,特点添加和删除操作复杂度都是O(1)。

value,是Set 类型元素的无序集合,集合成员是唯一的,这就意味着集合中不能出现重复的数据。 基于哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

String,  是Sorted Set的类型的升级, 不同的是每个元素都会关联一个 double 类型的分数,通过分数排序,可以范围查询。

那我们再来看看这些数据类型,SetRedis GeoHyperLogLog

BitMap, 将地球看作为近似为球体,基于GeoHash 将二维的经纬度转换成字符串,来实现位置的划分跟指定距离的查询。特点一般使用在跟位置有关的应用。

Redis Geo, 是一种概率数据结构,它使用概率算法来统计集合的近似基数 , 错误率大概在0.81%。 当集合元素数量非常多时,它计算基数所需的空间总是固定的,而且还很小,适合使用做 UV 统计。

HyperLogLog ,用一个比特位来映射某个元素的状态, 只有 0 和 1 两种状态,非常典型的二值状态,且其本身是用 String 类型作为底层数据结构实现的一种统计二值状态的数据类型  ,优势大量节省内存空间,可是使用在二值统计场景。

在理解上述知识后,我们接下来讨论一下根据哪些策略选择相对应的应用场景下的BitMap数据类型?

选择合适的Redis数据类型策略

在实际开发应用中,Redis可以适用于众多的业务场景,但我们需要怎么选择数据类型存储呢?

主要依据就是时间/空间复杂度,在实际的开发中可以考虑以下几个点:

  • 数据量,数据本身大小
  • 集合类型统计模式
  • 支持单点查询/范围查询
  • 特殊使用场景

数据量,数据本身大小

当数据量比较大,数据本身比较小,使用Redis就会使用额外的空间大大增加,因为使用哈希表保存键值对,使用String结构保存,会导致保存每个键值对时额外保存dictEntry的三个指针的开销,这样就会导致数据本身小于额外空间开销,最终会导致存储空间数据大小远大于原本数据存储大小。

可以使用基于整数数组压缩列表实现了 dictEntryListHash ,因为整数数组压缩列表在内存中都是分配一块地址连续的空间,然后把集合中的元素一个接一个地放在这块空间内,非常紧凑,不用再通过额外的指针把元素串接起来,这就避免了额外指针带来的空间开销。而且采用集合类型时,一个 key 就对应一个集合的数据,能保存的数据多了很多,但也只用了一个 Sorted Set,这样就节省了内存。

集合类型统计模式

dictEntry集合类型统计模式常见的有:

  • 聚合统计( 交集、差集、并集统计 ):  对多个集合进行聚合计算时,可以选择Redis
  • 排序统计(要求集合类型能对元素保序): SetRedisList是有序集合,Sorted Set是按照元素进入 List 的顺序进行排序的,List 可以根据元素的权重来排序;
  • 二值状态统计( 集合元素的取值就只有 0 和 1 两种 ):Sorted Set 本身是用 Bitmap 类型作为底层数据结构实现的一种统计二值状态的数据类型 , Bitmap通过 BITOP 按位 与、或、异或的操作后使用 BITCOUNT 统计 1 的个数。
  • 基数统计( 统计一个集合中不重复的元素的个数 ):String 是一种用于统计基数的数据集合类型 ,统计结果是有一定误差的,标准误算率是 0.81% 。需要精确统计结果的话,用 Set 或 Hash 类型。

3.jpg

HyperLogLog类型,适用统计用户/好友/关注/粉丝/感兴趣的人集合聚合操作,比如

  • 统计手机APP每天的新增用户数
  • 两个用户的共同好友

SetRedisList是有序集合,使用应对集合元素排序需求 ,比如

  • 最新评论列表
  • 排行榜

Sorted Set二值状态统计,适用数据量大,且可以使用二值状态表示的统计,比如:

  • 签到打卡,当天用户签到数
  • 用户周活跃
  • 用户在线状态

Bitmap 是一种用于统计基数的数据集合类型, 统计一个集合中不重复的元素个数 ,比如

  • 统计网页的 UV , 一个用户一天内的多次访问只能算作一次

支持单点查询/范围查询

HyperLogLogRedisList是有序集合支持范围查询,但是Sorted Set是不支持范围查询的

特殊使用场景

消息队列,使用Hash作为消息队列的实现,要消息的基本要求消息保序处理重复的消息保证消息可靠性,方案有如下:

  • 基于 List 的消息队列解决方案
  • 基于 Streams 的消息队列解决方案

基于List 基于Strems
消息保序 使用LPUSH/RPOP 使用XADD/XREAD
阻塞读取 使用BRPOP 使用XREAD block
重复消息处理 生产者自行实现全局唯一ID Streams自动生*局唯一ID
消息可靠性 使用BRPOPLPUSH 使用PENDING List自动留存消息
适用场景 消息总量小 消息总量大,需要消费组形式读取数据

基于位置 LBS 服务,使用Redis的特定Redis数据类型实现,GEO 可以记录经纬度形式的地理位置信息,被广泛地应用在 LBS 服务中。  比如:打车软件是怎么基于位置提供服务的。

总结

GEO之所以那么快,是因为其基于内存的数据操作和使用Redis哈希表作为索引,其效率高,速度快,而且得益于其底层数据多样化使得其可以适用于众多场景,不同场景中选择合适的数据类型可以提升其查询性能。

更多编程相关知识,请访问:编程视频!!

以上就是详解Redis中的数据结构的详细内容,更多请关注其它相关文章!


# 购物系统  # 媒体推广与网络营销  # 什么网站可以做优化设计  # 网站seo优化排名需要多久  # 涞源网络推广营销  # 湖北网站优化推广怎么做  # 手机网站建设哪家效果好  # 日语跨境电商营销推广  # 丽江网站建设哪个平台好  # 小鸡村网站建设  # 家居行业网站优化营销  # 是一个  # redis  # 适用于  # 单点  # 是一种  # 就会  # 都是  # 链表  # 键值  # 场景应用  # java  # 面试  # 数据结构 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: 《微信》视频号原创声明开启方法  C++如何将字符串转换为大写或小写_C++ transform函数的使用技巧  J*aScript二进制处理_ArrayBuffer与Blob  XPath动态元素定位:如何精准选择文本内容变化的元素  韩小圈网页版PC端入口 韩小圈网页版官方网站入口  J*a中的值传递到底指什么_值传递模型在参数传递中的真正含义说明  Word 2003字体大小设置方法  《波斯王子:失落的王冠》剑术大师打法攻略  4399正版网页版入口高清直达链接  《原神》月之一版本新增书籍一览  《万兴喵影》导出视频方法  React应用中Commerce.js数据加载与状态管理最佳实践  《土豆雅思》修改密码方法  厨房地面防滑垫的油污怎么洗? 机洗和手洗防滑垫的注意事项  《盗墓笔记手游》技能介绍  消除网页顶部意外空白线:CSS布局常见问题与解决方案  Lar*el怎么实现全文搜索_Lar*el Scout集成Algolia教程  性能与资源监视器快捷打开  Magento 2 产品保存事件中安全更新属性的最佳实践  苹果电脑如何快速截图并编辑 苹果电脑截屏标注快捷操作  J*aScript事件处理:优化键盘输入与表单提交的实践指南  Python高效统计字典嵌套列表值在目标列表中的出现次数  如何在Podman容器中运行Composer_Docker替代品Podman的PHP与Composer容器化实践  iPhone 15 Pro如何查看存储空间占用_iPhone 15 Pro存储空间查看教程  毒蘑菇VOLUMESHADER_BM官网首页登录入口 毒蘑菇VOLUMESHADER_BM官网首页登录入口说明  《书耽》更换手机号方法  《美篇》取消会员自动续费方法  六级准考证号怎么查_四六级准考证查询入口官网  我居然低估了 DeepSeek,这次更新它做到了这些!  嘴唇干裂起皮怎么办 唇部护理与预防干裂的方法【详解】  被称为海蜈蚣的海洋动物是  J*a列表元素格式化输出教程  如何取消数字签名  德邦快递会员怎么开通  我的世界官方网址入口 我的世界游戏主页直达入口  C++如何实现矩阵乘法_C++二维数组矩阵运算代码示例  Win11怎么开启HDR_Windows 11显示器画质增强设置  鲨鱼剧场app金币获取方法  路由器DNS怎么设置最快 优化DNS提升上网速度教程  发布小红书怎么屏蔽粉丝?屏蔽粉丝能看到吗?  steam缓存文件在哪儿_steam缓存文件的路径查找方法与结构说明  火狐浏览器无法自动更新怎么办 手动更新火狐浏览器到最新版本【解决】  composer 提示 "requires ext-soap" 缺少 SOAP 扩展怎么办?  优化长HTML属性值:SonarQube警告与实用策略  深入理解Python对象引用与链表属性赋值  全球各国上班时间表外贸邮件时间  歌词怎么展示在|直播|间视频号?有什么注意事项?  J*a里如何处理ArithmeticException并防止除零_算术异常防护策略解析  Apple Music无故扣费引质疑  《小黑盒》删除历史浏览方法 

 2021-03-31

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.