Redis数据结构

数据类型和数据结构

  数据结构为:简单动态字符串、双向链表、压缩列表、哈希表、跳表和整数数组
数据类型和数据结构对应的关系为:

  • String(简单动态字符串):存储数字,采用int类型编码;非数字,则采用raw编码。应用场景:缓存用户信息,将用户ID作为键,用户信息的字符串作为值存储在Redis中;计数器:统计文章浏览量,将文章ID作为键,使用字符串结构存储文章的浏览量,每次有用户浏览文章时,通过对应的键对浏览量进行自增操作
  • List(双向链表O(N)、压缩列表):字符串长度及元素个数小于一定范围使用ziplist编码,条件不满足,则转换为linkedlist编码。应用场景:使用Redis的列表结构可以方便地实现社交媒体最新消息的排行,例如:将每条消息的内容作为字符串插入到Redis列表的头部,限制列表的长度为固定值,当超过指定长度时,自定删除最旧的消息,从而保持最新消息的更新
  • Hash(压缩列表、哈希表):相当于Java中的HashMap,hash的实现与Java中HashMap(JDK1.7)的结构是一致的。应用场景:用户个人信息管理,使用Redis的哈希表结构可以方便地实现用户信息的存储和访问
  • Set(哈希表O(1)、整数数组O(N)):相当于Java中的HashSet,保存元素为整数且元素个数小于一定范围使用int set编码,任意条件不满足,使用hashtable编码。应用场景:将每个用户的好友列表存储在Redis集合中,使用集合操作可以快速判断两个用户是否是好友,还可以进行好友推荐等功能
  • Sorted Set(压缩列表O(N)、跳表O(logN)):Zset是Redis中最常问的数据结构,类似于Java中SortedSet和HashMap的结合体,一方面通过set来保证内部value值得唯一性,另一方面通过value的权重来进行排序。排序功能通过跳表实现。Zset对象中保存的元素个数小于且成员长度小于一定值使用ziplist编码,任意条件不满足,使用skiplist编码。应用场景:排行榜。使用Redis的有序集合可以方便地实现排行榜功能。例如将每首歌曲的名称作为字符串元素,播放次数作为分数存储在有序集合中,可以按照播放次数进行排序,快速获取热门歌曲的排行
(1)简单动态字符串(SDS)

  SDS 在内部用一个结构体来表示,结构体中至少包含两个字段:len(已占用字节数)和alloc(已分配总字节数),以及一个字节数组buf来存储实际的数据和结束符’\0’(结束符会额外占用1个字节)。

  • 与C语言Char类型的区别:
    • SDS在修改字符串时,会先检查长度是否够长,不够会进行扩展,避免缓冲区溢出。可以预防缓冲区溢出并支持自动扩容。
    • C语言Char类型仅能表示单个字符或组成以’\0’结束的字符串,对于字符串的修改和管理需要手动处理内存分配、长度计算以及防止溢出等问题。
典型应用场景
  • 数据缓存:Redis作为数据缓存层,MySQL作为数据存储层。应用服务器首先从Redis中获取数据,如果缓存层中没有,则从MySQL中获取后先存入缓存层再返回给应用服务器。
  • 计数器:在Redis中写入一个value为数值型的key作为平台计数器、视频播放计数器等。每个有效客户端访问一次,或视频每播放一次,都是直接修改Redis中的计数器,然后再以异步方式持久化到其他数据源中,例如持久化到MySQL中
  • 共享Session
    在这里插入图片描述

  对于一个分布式应用系统,如果将类似用户登录信息这样的session数据保存在提供登录的服务器中,那么如果用户再次提交像收藏、支付等请求时可能会出现问题:在提供收藏、支付等服务的服务器中并没有该用户的Session数据,从而导致该用户需要重新登录,对于用户来说,这是不能接受的。

  此时,可以将系统中所有用户的Session数据全部保存在Redis中,用户在提交新的请求后,系统先从Redis中查找相应的Session数据,如果存在,则再进行相关操作,否则跳转到登录页面,这样就不会引发“重新登录”问题。

  • 限速器:很多平台为了防止Dos(Denial of Service, 拒绝服务)攻击,一般都会限制一个IP不能在一秒内访问超过n次,而Redis可以结合key的过期时间与incr命令来完成限速功能,充当限速器。注意:无法防止DDos(Distributed Denial of Service, 分布式拒绝服务)攻击。
// 客户端每提交一次请求,都会执行下面的代码
// 等价于set 192.168.192.55 1 ex 60 nx
// 指定新IP作为key的缓存过期时间为60秒
Boolean isExists = redis.set(ip, 1, "EX 60", "NX");
// 成功了不为null或者1分钟之内请求次数超过5次,直接限流
if(isExists != null | redis.incr(ip) <= 5){
    // 通过
}else{
    // 限流
}
(2)链表

  Redis使用的是双向无环链表,具有以下特点:

  • 双端:Redis的链表是双向的,链表中每个节点具有前置节点和后置节点的引用。这种设计允许从链表的两端都能很快地插入和删除元素,并且可以双向遍历链表。
  • 无环:表头节点的prev指针和表尾节点的next指针都指向NULL
  • 带链表长度计数器:通过len属性获取链表长度的时间复杂度为O(1)
  • 多态:链表节点除了包含前后节点指针外,还会存储具体的数据。在Redis的链表应用场景中,节点通常用于存储Redis对象,比如字符串、整数值或其他复合类型的数据结构。
      用途:(1)发布/订阅系统:用于存储频道订阅者和模式订阅者;(2)客户端状态管理:Redis服务器使用链表维护客户端状态信息。
(3)压缩列表

  Redis中的压缩列表是一种特殊的线性数据结构,并不是利用某种算法对数据进行压缩,设计目的是为了存储小尺寸数据时尽可能节省内存空间。Redis使用压缩列表作为列表键,哈希键以及其他一些内部数据结构的底层实现,特别是在数据项较少或数据大小较小的情况下。
  压缩列表由一系列特殊编码的连续内存块组成的顺序型数据结构,组成结构如下:
| zlbytes | zltail | zllen | entry1 | entry2 | … | entryN | zlend |

  • zlbytes:记录整个压缩列表占用的内存字节数,4个字节;
  • zltail:记录最后一个entry的偏移量,有助于从尾部开始遍历,4字节;
  • zllen:记录entry的数量,2字节,如果entry数量小于2^16,可以直接从这里读取,否则需要遍历整个列表计算。
  • entry:可以存储两种类型的数据:整数值和字节数组。整数值会根据大小选择合适的编码方式,以节省空间,可以是一个字节、两个字节、三个字节等;字节数组则包括前置长度编码和实际的内容数据。

  查找第一个和最后一个元素,可以通过表头三个字段直接定位,时间复杂度为O(1),其余元素,时间复杂度则为O(N)

(4)跳表

  Redis中的跳表是一种高度优化的有序数据结构,用于高效地实现插入、删除和查找操作,特别适合于对大量数据进行排序和检索的场景。跳表在Redis中最典型的应用是作为有序集合的底层数据结构。
  跳表的基于思想源于链表,在此基础上引入了多级索引的概念。在一个普通链表中,查找、插入和删除操作的时间复杂度为O(n)。在跳表中,通过在链表的节点上增加多层索引结构,形成一个多层的链表结构,每层都是一个有序链表,高层链表的节点稀疏分布在底层链表的节点之间,并通过指针相互连接。

在这里插入图片描述

  在跳表中,插入操作的时间复杂度之所以能够维持在O(logn)级别,是因为其查找插入位置的过程依托于其独特的多层次索引结构。在插入新数据时,首先会在最高层级进行高效的跳跃式查找,找到合适的位置后,逐步下沉至底层链表进行精确插入。为了保持数据有序性和跳表的高效性,每当我们在底层链表中插入一个节点时,都会依据一定的随机策略决定该节点是否应当出现在更高的层级作为索引节点。这样做的目的在于,即使在持续插入大量数据的过程中,也能通过适时地增加索引节点,避免因数据分布不均而导致的性能退化现象,进而防止在极端情况下跳表变成单链表,丧失其快速查找的优势。

  对于删除操作而言,根据待删除节点在跳表中的位置及其索引属性有所不同:

  • 若待删除节点仅存在于底层数据链表中,无需更改索引结构,可以直接在相应位置进行节点删除操作。
  • 若待删除节点同时出现在索引层级,那么在执行删除时,不仅要从底层数据链表中移除该节点,还要同步删除其在各索引层级中的对应引用,确保整个跳表结构的完整性和查找效率不受影响。这就涉及到在多个层级中逐一查找并移除该节点,尽管如此,由于跳表的索引特性,这一过程仍然保持在平均时间复杂度为O(log n)的理想水平。
  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值