一、缓存
我们一提到redis,自然而然就想到缓存,国内外中大型的网站都离不开缓存。合理的利用缓存,比如缓存热点数据,不仅可以提升网站的访问速度,还可以降低数据库DB的压力。
缓存是 redis 出镜率最高的一种使用场景,仅仅使用 set/get 就可以实现,不过也有一些需要考虑的点:
- 如何更好地设置缓存
- 如何保持缓存与上游数据的一致性
- 如何解决缓存血崩,缓存击穿问题
二、排行榜
当今互联网应用,有各种各样的排行榜,如电商网站的月度销量排行榜、社交APP的礼物排行榜、小程序的投票排行榜等等。Redis提供的zset数据类型能够实现这些复杂的排行榜。
比如,用户每天上传视频,获得点赞的排行榜可以这样设计:
- 用户Jay上传一个视频,获得6个赞
- 过了一段时间,再获得一个赞,可以这样:
- 如果某个用户John作弊,需要删除该用户
- 展示获取赞数最多的3个用户:
三、计数器应用
各大网站、APP应用经常需要计数器的功能,如短视频的播放数、电商网站的浏览数。这些播放数、浏览数一般要求实时的,每一次播放和浏览都要做加1的操作,如果并发量很大对于传统关系型数据的性能是一种挑战。Redis天然支持计数功能而且计数的性能也非常好,可以说是计数器系统的重要选择。
- 频率控制:接口防刷,密码次数尝试限制。
- 数量统计:请求量统计。比如业务需求中经常有限制一个手机号一天限制发送n条短信,一个接口一分钟限制多少请求、一个接口一天限制调用多少次等等。
- 数量控制器:商品抢购,奖励额度控制。
Redis incr 可以实现原子性的递增:
四、共享Session
如果一个分布式Web服务将用户的Session信息保存在各自服务器,用户刷新一次可能就需要重新登录了,这样显然有问题。实际上,可以使用Redis将用户的Session进行集中管理,每次用户更新或者查询登录信息都直接从Redis中集中获取。
五、分布式锁
几乎每个互联网公司中都使用了分布式部署,分布式服务下,就会遇到对同一个资源的并发访问的技术难题,如秒杀、下单减库存等场景。
- 用synchronize或者reentrantlock本地锁肯定是不行的。
- 如果是并发量不大话,使用数据库的悲观锁、乐观锁来实现没啥问题。
- 但是在并发量高的场合中,利用数据库锁来控制资源的并发访问,会影响数据库的性能。
- 实际上,可以用Redis的setnx来实现分布式的锁。
六、消息队列
消息队列是大型网站必用中间件,如ActiveMQ、RabbitMQ、Kafka等流行的消息队列中间件,主要用于业务解耦、流量削峰及异步处理实时性低的业务。Redis提供了发布/订阅及阻塞队列功能,能实现一个简单的消息队列系统。另外,这个不能和专业的消息中间件相比。
在Redis中,List类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素。
6.1 List命令行操作实现队列
另外调用rpop命令或者lpop命令才可以实现不停的监听且消费消息。
redis提供了阻塞命令 brpop和blpop实现消费者阻塞读取。
- 生产者客户端
- 消费者客户端
6.2 发布/订阅
"发布/订阅"模式包含两种角色,分别是发布者和订阅者。订阅者可以订阅一个或者多个频道(channel),而发布者可以向指定的频道(channel)发送消息,所有订阅此频道的订阅者都会收到此消息。
- 发布消息
发布者发布消息的命令是
publish,用法是publish channel message
- 订阅频道
订阅频道的命令是
subscribe,可以同时订阅多个频道,用法是subscribe channel1 [channel2 ...]
红字部分表示成功的收到消息(依次是消息类型,频道,消息内容)
6.3 按照规则发布/订阅
除了可以使用subscribe命令订阅指定的频道外,还可以使用psubscribe命令订阅指定的规则。规则支持通配符格式:psubscribe pattern [pattern ...]订阅多个模式的频道。
通配符中:
- ?表示1个占位符
- *表示任意个占位符(包括0)
- ?*表示1个以上占位符
七、位操作
用于数据量上亿的场景下,例如几亿用户系统的签到,去重登录次数统计,某用户是否在线状态等等。腾讯10亿用户,要几个毫秒内查询到某个用户是否在线,能怎么做?千万别说给每个用户建立一个key,然后挨个记(你可以算一下需要的内存会很恐怖,而且这种类似的需求很多。这里要用到位操作——使用setbit、getbit、bitcount命令。原理是:redis内构建一个足够长的数组,每个数组元素只能是0和1两个值,然后这个数组的下标index用来表示用户id(必须是数字哈),那么很显然,这个几亿长的大数组就能通过下标和元素值(0和1)来构建一个记忆系统。
- SETBIT key offset value 设置指定key的值在offset处的bit值,offset从0开始。返回值为在offset处原来的bit值
# 通过位操作将 h 改成
i127.0.0.1:6379> SET h h # 二进制为 01101000
OK
127.0.0.1:6379> SETBIT h 7 1 # 将最后一位改成1 => 01101001
(integer) 0
127.0.0.1:6379> GET h
"i"
- GETBIT key offset 获取指定key的值在offset处的bit值,offset从0开始。如果offset超出了当前位图的范围,则返回0。
127.0.0.1:6379> set i i # 二进制为 01101001
OK
127.0.0.1:6379> getbit i 0 # 第1位为0
(integer) 0
127.0.0.1:6379> getbit i 1 # 第2位为0
(integer) 1
127.0.0.1:6379> getbit i 7 # 第8位为0
(integer) 1
- BITCOUNT key [start end] 统计指定key值中被设置为1的bit数。可以通过指定参数star和end来限制统计范围。
注意,这里的star和end不是指bit的下标,而是字节(byte)的下标。比如start为1,则实际对应的bit下标为8(1byte = 8 bit)
127.0.0.1:6379> set hi hi # 二进制为 0110100001101001
OK
127.0.0.1:6379> bitcount hi # 所有是1的位数:7个
(integer) 7
127.0.0.1:6379> bitcount hi 1 2 # 即统计 01101001 中1的位数
(integer) 4