深入学习Redis数据结构

redis是基于内存的数据库,对数据的读写操作都是在内存中完成的,操作速度快。常用于缓存,消息队列,分布式锁等场景

数据结构:常见数据类型:String,Hash,list,set,Zset

新支持:BitMap,HyperLogLog,GEO,Stream

1.String内部实现:

采用int 加SDS(简单动态字符串)来实现。

如果为整数 则在ptr中存储值,并将编码设置改为int

如果为字符串小于等于32字节(2.*版本)会将字符串对象使用一个简单动态字符串(SDS)来保存,将编码方式设置为embstr. 大于32字节则设置为raw。

应用场景

缓存,计数(点赞,转发,库存数量等等。),分布式锁,共享session

2.List类型内部实现:

3.2之前采用压缩头部与双向链表的方式,之后采用quicklist实现。

引用场景:

消息队列:(list和Stream都可以实现)

需求:1.消息的保序2.处理重复消息3.保证消息的可靠性

  1. 如何保证消息的保序需求:

list本身就是先进先出的顺序对数据进行存储,因此能够满足消息的保序性。

使用LPUSH/RPOP || RPUSH/LPOP来实现。

缺陷:消费者不知道什么时候有消息传入,那么就需要一直使用RPOP,成功则拿到消息,失败就返回空值接着 循环,导致cpu一直消耗在RPOP上,带来不必要的性能损耗

解决方案:redis官方提供了了BRPOP,阻塞式读取,客户端没有读取到队列数据时,自动阻塞,直到有新的数据写入队列,再重新读取

        2. 如何处理重复的命令

要求:1.每个消息有个全局的ID 2.消费者记录已经处理过的ID,拿到消息之后将消息ID进行比对,没有处理就 处理,处理了就不再做处理。

但是List不会对消息主动生产ID,需要我们自己添加。

        3.保证消息的可靠性

        当消费者读取到消息后,list会删除消息。如果消费者在处理消息的过程中电脑宕机或故障,导致没有处理完消息,在电脑恢复之后消息就无法再次从list中读取

        List类型提供了BRPOPLPUSH命令,将消费者程序从一个List中读取消息,同时放入另一个List中留存。那么电脑回复之后还可以再此list中读取。

缺陷:

无法多个消费者消费同一条消息。如果要消费同一个消息那么必须将多个消费者组成消费组,使得多个消费者消费同一个消息,但是List类型并不提供消费组的实现。而Stream能够满足三大需求的同时,还支持消费组的消息读取。

Hash

Hash是一个键值对(key-value)集合,其中value的格式为value=[{field1,value1},...{fieldN,valueN}]。

Hash底层实现数据结合为压缩列表或哈希表实现,在7.0之后采用listpack数据结构来实现

# 存储一个哈希表key的键值
HSET key field value   
# 获取哈希表key对应的field键值
HGET key field
​
# 在一个哈希表key中存储多个键值对
HMSET key field value [field value...] 
# 批量获取哈希表key中多个field键值
HMGET key field [field ...]       
# 删除哈希表key中的field键值
HDEL key field [field ...]    
​
# 返回哈希表key中field的数量
HLEN key       
# 返回哈希表key中所有的键值
HGETALL key 
​
# 为哈希表key中field键的值加上增量n
HINCRBY key field n                         
​
应用场景:

缓存对象

购物车,以用户id为key,商品id为field,商品数量为value。当前仅仅是将商品ID存储到了Redis中,在回显商品具体信息的时候,还需要去数据库中用商品id去查询到完整的商品信息。

Set类型

set类型是一个无序且唯一的键值集合,它的存储顺序不会按照插入的先后顺序来存储。

一个集合支持存储2^32-1个元素。且支持多个集合取交集,并集,差集。

内部实现:

采用哈希表或整数集合实现

  • 集合中元素都是整数且小于512个会使用整数集合作为Set类型的底层数据结构

  • 如果不满足就使用哈希表来作为底层数据结构。

应用场景:

特点:无序,不可重复,支持并交集等操作

Set适合用来数据去重,且保证唯一性,还可以用来统计多个集合的交集等。

但是!!!Set的差集、并集、交集的计算复杂度较高,在数据量较大的情况下会导致Redis实例阻塞。

在主从集群中,为了避免主库因为Set做聚合运算时导致主库阻塞,可以选择一个从库来完成聚合统计,或者把数据返回为客户端,由客户端来完成。

点赞:文章id为key,value为用户id

共同关注:用来计算共同好友等。

抽奖活动:key为抽奖名,value为参与人员。

Zset

Zset相比于Set类型多了一个排序属性score,每个元素相当于有两个值组成,一个为有序集合的元素值,一个是排序值。保留了set类型中不能包含重复成员的特性。

内部实现:

由压缩列表或调表来实现

  • 如果有序集合个数小于128个,且每个元素小于64字节,redis会使用压缩列表的形式。

  • 不满足则使用调表

在7.0中,压缩列表已经废弃,交由listpack数据结构实现。

应用场景:

Zset 类型(Sorted Set,有序集合) 可以根据元素的权重来排序,可以根据元素的权重来排序,可以自己来决定每个元素的权重值。

当需要展示最新列表,排行榜等场景时,数据更新频繁或者需要分页显示,可以优先考虑使用Sorted Set

排行榜:

电话,姓名排序

Stream

专门为消息队列设计的数据类型。

在Stream没出来之前,消息队列的实现方式都有着缺陷

  • 发布定略模式,不能持久化也就无法可靠的保存消息,并且对于离线重连的客户端不能读取消息的缺陷

  • List实现消息队列不能重复消费,一个消费消息完就会被删除,而且生产者需要自行实现全局唯一ID

基于以上问题,Stream完美的实现消息队列,支持消息的持久化,支持函自动生成全局唯一ID,支持ack确定到的模式、支持消费组模式等。

命令
  • XADD:插入消息,保证有序,可以自动生成全局唯一 ID;

  • XLEN :查询消息长度;

  • XREAD:用于读取消息,可以按 ID 读取数据;

  • XDEL : 根据消息 ID 删除消息;

  • DEL :删除整个 Stream;

  • XRANGE :读取区间消息

  • XREADGROUP:按消费组形式读取消息;

XPENDING 和 XACK:

  • XPENDING 命令可以用来查询每个消费组内所有消费者「已读取、但尚未确认」的消息;

  • XACK 命令用于向消息队列确认消息处理已完成;

应用场景:

消息队列:

  • 消息保序:XADD/XREAD

  • 阻塞读取:XREAD block

  • 重复消息处理:Stream 在使用 XADD 命令,会自动生成全局唯一 ID;

  • 消息可靠性:内部使用PENDING List自动保存信息,使用XPENDING命令查看消费组已经读取但是未被确认的消息,消费者使用XACK确认消息

  • 支持消费组形式消费数据

Redis作为消息队列来使用面临两个问题:

  • redis本身可能会丢数据(AOF持久化中为异步写盘,Redis宕机时会存在数据丢失的可能。主从复制也是异步,主从切换时也可能存在丢失数据的可能)

  • 面对消息挤压,内存资源会紧张

    • Redis 的数据都存储在内存中,这就意味着一旦发生消息积压,则会导致 Redis 的内存持续增长,如果超过机器内存上限,就会面临被 OOM 的风险。Redis提供了可以指定队列最大长度的功能,避免这种情况发生,当达到上限后,旧消息会被删除,只保留固定长度 新消息,那么就有可能丢失消息。

    • 而kafka、RabbitMQ会写在磁盘上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值