Redis在美团面试中的多重应用:不止是缓存,能否作为消息队列的深入分析
在面试中,Redis的应用场景是一个常见的基础题目,主要考察应聘者对Redis的多种用途的了解。
即使您不打算面试,这些知识在实际开发中也是非常有价值的。
内容来概览:
Redis的多种应用场景
- 分布式锁:利用Redis实现分布式锁是一种广泛采用的方案。通常,我们会基于Redisson来实现这一功能。
- 限流:通常通过结合Redis和Lua脚本实现限流。
- 消息队列:Redis提供的List数据结构可以简单地作为一个队列使用。在Redis 5.0版本中引入的Stream类型更加适合用作消息队列,类似于Kafka,具有主题和消费组的概念,支持消息持久化和确认机制。
- 延时队列:Redisson提供内置的延时队列,基于Sorted Set实现。
- 分布式会话:可以利用String或Hash数据类型来保存会话数据,所有服务器均可访问。
- 复杂业务场景:通过Redis及其扩展(如Redisson)提供的数据结构,我们可以轻松完成许多复杂的业务逻辑,比如使用Bitmap统计活跃用户,以及通过Sorted Set维护排行榜。
- ……
Redis如何实现分布式锁?
Redis是否可以作为消息队列?
在实际项目中,Redis作为消息队列的使用并不普遍,因此了解这一知识点即可。
结论是:可以使用Redis作为消息队列,但并不推荐。与专用的消息队列相比,Redis存在许多不足之处。
在Redis 2.0之前,若希望使用Redis作为消息队列,仅能通过List实现。
使用 RPUSH/LPOP
或 LPUSH/RPOP
可以实现简单的消息队列:
# 生产者生产消息
> RPUSH myList msg1 msg2
(integer) 2
> RPUSH myList msg3
(integer) 3
# 消费者消费消息
> LPOP myList
"msg1"
然而,使用 RPUSH/LPOP
或 LPUSH/RPOP
的方式存在性能问题,我们需要频繁进行轮询来调用 RPOP
或 LPOP
来消费消息。当List为空时,大多数轮询请求都是无效的,从而浪费系统资源。
因此,Redis提供了BLPOP
和BRPOP
等阻塞式读取命令(以B-Blocking开头的命令均为阻塞式),并支持超时参数。如果List为空,Redis服务端不会立即返回结果,而是等待List中有新数据后再返回,或者在最多一个超时时间后返回。如果将超时时间设置为0,则会无限期等待,直到弹出消息。
# 超时时间为 10秒
# 如果有数据立即返回,否则最多等待10秒
> BRPOP myList 10
null
List实现的消息队列功能过于简单,像消息确认机制等功能需要我们自己实现,而最致命的是缺乏广播机制,消息只能被消费一次。
Redis 2.0引入了发布/订阅(pub/sub)功能,以解决List实现消息队列时没有广播机制的问题。
Redis的发布/订阅(pub/sub)功能
在pub/sub中引入了一个概念,称为频道(channel),其实现便是基于该频道。
pub/sub涉及发布者(Publisher)和订阅者(Subscriber,也称消费者)两个角色:
- 发布者通过
PUBLISH
将消息发送到指定频道。 - 订阅者通过
SUBSCRIBE
订阅其关注的频道,订阅者可以订阅一个或多个频道。
我们可以启动三个Redis客户端进行简单演示:
发布/订阅实现消息队列的演示
pub/sub既支持单播也支持广播,同时还支持频道的简单正则匹配。但消息丢失(如客户端断开连接或Redis宕机都会导致消息丢失)、消息堆积(发布者发布消息时不考虑消费者的处理能力)等问题依然没有得到良好的解决。
为此,Redis 5.0引入了一个新的数据结构Stream
,以更好地支持消息队列功能。Stream
支持:
- 发布/订阅模式
- 按消费者组进行消费
- 消息持久化(RDB和AOF)
虽然Stream
在使用上稍显复杂,这里不做演示。而且在实际使用中,Stream
依然存在一些小问题,比如在Redis故障恢复后,不能确保消息至少被消费一次。
综上所述,与专业的消息队列相比,使用Redis来实现消息队列依然存在许多不足之处,如消息丢失和堆积问题难以解决。因此,我们通常建议避免使用Redis作为消息队列,而是选择市场上成熟的消息队列产品,如RocketMQ或Kafka。