Redis面试题-场景
目录索引
1. 缓存
Redis实现缓存功能的基本原理是将常用的数据存储在内存中,以加快数据访问速度,并且可以通过设置过期时间来自动淘汰过期的缓存数据。适合缓存的数据是那些更新频率较低,访问频率较高的数据,例如商品信息,用户信息等。
2. 延迟队列
延迟队列是一种用于处理延迟消息的队列,它的主要特点是能够在指定的时间间隔后消费消息(执行任务)。基本上类似一个任务调度服务,只是处理的对象是消息而不是任务,常见使用场景有以下几种:
- 在购物平台下单,超时未成功付款,订单进行自动取消
- 打车时,规定时间内没有车主接单,订单进行自动取消
实现方案
- 在redis中可以使用有序集合(ZSet)来实现延迟消息队列,ZSet有一个Score属性可以用来存储延迟执行的时间。
- 使用
zadd score1 value1
命令就可以一直往内存中生产消息,再利用zrangebyscore
查询复合条件的所有待处理的任务,通过循环执行队列任务即可。
存在问题
- 消息没有持久化,如果服务器宕机或重启,消息可能会丢失
- 没有ACK机制,如果消费失败,消息会丢失。有大佬实现了个支持ACK的 🔗 用 Redis 做一个可靠的延迟队列
3. 消息队列
常见消息队列选型Kafka、RocketMQ等服务相对Redis比较重,对于一些简单的,没有大量消息堆积的非关键业务场景可以使用Redis来实现消息队列。
Redis中可以使用List、Stream、Pub/Sub 来实现简单的消息队列
List实现MQ
Redis队列是简单的字符串列表,按照插入顺序排序,你可以添加一个元素到列表的头部(左边)或者尾部(右边)。通过使用以下命令,可以实现一个简单的消息队列功能:
- LPUSH、RPOP 左进右出
- RPUSH、LPOP 右进左出
使用RPOP、LPOP命令消费数据时有个问题就是需要消费者轮询Redis,所以可以使用BRPOP、BLPOP避免这个问题。
实现ACK机制
Redis中可以使用双队列来实现ACK机制,步骤如下:
- 准备两个队列,其中存储数据的队列为
queue1
,另一个队列名为queue1_bak
- 消费者使用
RPOPLPUSH
① 或BRPOPLPUSH
命令消费数据(数据在弹出的同时将备份到另一个 bak 队列) - 消费者消费数据成功后,使用
LREM
命令消费 bak 队列的数据 - 启用定时任务,使用
LRANGE
命令读取队列数据,解析每条数据(需要包含产生时间戳),将超市消息(认为消费失败)重新入队queue1
- 超时定义:因为使用redis的队列场景一般不存在大量消息堆积,所以可以简单定一个时间
- 因为这里的超时定义不严谨,所以建议消息中包含唯一ID实现幂等消费,否则可能会重新消费
Stream实现MQ
Stream是Redis5.0引入的一种专门为消息队列设计的数据结构,Stream是一个包含0个或者多个元素的有序队列,这些元素根据ID的大小进行有序排列,它实现了大部分消息队列的功能:
- 消息ID序列化生成
- 消息遍历
- 消息的阻塞和非阻塞读
- Consumer Groups(消费组)
- 消费组的目的是通过多个消费者同时消费一个队列,实现负载均衡和容错
- 通过
XGROUP
/XREADGROUP
/XACK
实现消费组功能
- ACK确认机制
- 支持多播
- 提供了很多消息队列操作命令
- 提供了消息持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失
参考
发布订阅实现MQ
TIP
严格来说,“发布订阅”只是一个广播机制,而不是真正的消息队列,因为不支持消息累积,从而无法实现MQ必备的异步通信功能。
Redis通过PUBLISH
、SUBSCRIBE
等命令实现了订阅与发布模式,这个功能提供两种信息机制,分别是订阅 / 发布
到频道和订阅 / 发布
到模式(一个类似正则表达式的key)
订阅 / 发布
模式包含两种角色,分别是发布者和订阅者,订阅者可以订阅一个或多个channel
,而发布者可以向指定的channel
发送消息,所有订阅此频道的订阅者都会收到此消息
缺点
由于Redis不会存储消息,所以只有在线的订阅者可以实时接收消息,并且没有ACK机制,离线订阅者会永远丢失消息