使用 Redis 可以做哪些很酷的事情?
一、基于 zset
1.1 排行榜
- 我们在使用Redis中的有序集合
zset
来实现排行榜时,原理很简单,即zset 中的member 是候选人,score 是排行榜中的名次; - 因为每个选手的唯一性,集合中的元素也具有唯一性;
- 排行榜中中选手的名次可直接对应zset 中的 score;
- 排行榜中的名次可能是动态变化的,zset 是Redis提供的数据结构,在内存中维护,进行更新时效率很高;
1.2 滑动窗口限流
- 使用 zset 实现滑动窗口限流的原理也不复杂。例如,我们可以将需要限流的接口的
{method} + {URL}
作为获取某个 zset 的key,该zset 的score 为时间戳,member 为userId 或者 请求详情等; - 超过某个滑动窗口之外的数据使用
ZREMRANGEBYSCORE
命令进行删除; - 使用
ZCARD
命令统计时间窗口内的请求数,该数量超过阈值即进行限流;
1.3 延时队列 / 消息
- 使用
zset
实现延时队列的原理是:我们把想某个要执行的任务的 taskId 作为某个任务的member,该任务要执行的时间为 score,定时扫描该zset,如果当前时间的时间戳 > score,取出该taskId,执行对应的任务; - 举个例子,某个任务 (taskId为001) 需要在2分钟之后执行,那么就在某个 zset 中存放 (t001, {timestamp1}+2min),异步线程会周期性扫描该zset,如果发现当前时间的时间戳 {timestamp2} > timestamp1}+2min,取出该任务ID—t001,找到对应的任务,执行;
- 弊端:延时任务的执行最多会有扫描周期时间段长度的延时;
- 或者使用 MQ 中的消息队列;
二、基于 pub-sub
2.1 消息队列
- 在Redis客户端中,我们可以通过
SUBSCRIBE
命令创建/获取指定 channel 的消息; - 通过
PUBLISH
命令,向指定的 channel 中发送消息;
三、基于 setnx
3.1 分布式锁
- 使用Redis中的
setnx
命令可以实现分布式锁,但是不能设置锁的过期时间,如果某个线程持有了锁,但是执行过程出现错误,导致锁无法释放,会影响正常业务; - 可使用
set
命令,启用-nx
选项,并设置过期时间。生产上,更推荐Redisson 中包装好的实现,其提供了看门狗线程,可自动给锁续期,避免任务未执行完锁就被释放掉的问题。 - 如果追求分布式锁的可用性,避免Redis的单点故障,可使用Redis 中内置实现的分布式锁——RedLock,避免分布式锁的单点故障问题。