文章目录

  • redis可以做什么
  • 总结
  • 异步消息队列
  • 分布式锁冲突处理
  • 延时队列的实现
  • 位图
  • HyperLogLog
  • 布隆过滤器
  • 简单限流-滑动窗口
  • 漏斗限流
  • 大海捞针scan
  • keys reg缺点
  • scan原理
  • scan特点
  • scan使用


redis可以做什么

记录帖子的点赞数,评论数,点击数
记录用户帖子id列表,便于快速显示用户的帖子列表
记录帖子的标题,摘要,作者和封面信息,用于列表页显示
记录帖子的点赞用户id列表,评论id列表,用于显示和去重计数
缓存近期热帖内容,减少数据库压力
如果帖子ID是自增的,使用redis分配帖子ID
帖子和收藏集的关系
记录总热榜,分类热榜帖子ID列表
缓存用户行为历史,进行恶意行为过滤

总结

1. 利用list列表实现的 异步消息队列
2. 利用zset有序集合实现的滑动窗口控制异常行为历史,实现的延时队列处理分布式锁冲突
3. 利用字符串实现的位图统计每天的签到数等
4. 利用不精确set集合实现的HyperLogLog进行大数据去重计数,实现的布隆过滤器进行exists操作
5. scan代替keys,因为其实现了limit语义,而且具有分步查询不卡顿的优点

异步消息队列

没有确认机制
rpush/rpop:从右边入队,从右边出队
lpush/lpop:从左边入队,从左边出队

  1. 问题:出队时队列空了怎么办??
    拉高客户端的cpu,redis的qps也被拉高
    解决:使用sleep睡上1秒,让出处理器资源,但会增大消息的延迟
    分析:仅有一个消费者,延迟就是1秒,若有多个消费者,延迟会下降
    优化解决:使用brpop代替rpop,若没有数据,则立刻阻塞读线程
  2. 问题:若一直阻塞在那,则导致空闲连接问题
    redis服务器一般会主动断开连接,这时brpop会抛出异常
    解决:编写消费者要捕获异常,然后进行重试

分布式锁冲突处理

客户端请求分布式锁,没加成功

  1. 直接抛异常,同时用户稍后再试 适合用户直接发起的请求
  2. 睡一会再重试 不推介,会导致后续消息处理延时
  3. 将请求转移到延时队列,过一会重试

延时队列的实现

通过redis的zset有序集合来实现。将消息作为value,消息到期处理时间为score,然后多线程获取到期任务进行处理,但要处理并发争抢任务问题。

消息:内部类static class Task{id,msg}
延时队列:RedisDelayQueue{jedis,queueKey}
延时操作:delay(T msg){jedis.zadd(queueKey,currentTime+500,task)}
多线程处理过期任务:loop(){}
注释:
生成任务id:UUID.randomUUID().toString()
序列化消息:JSON.toJSONString(task)
反序列化消息:JSON.parseObject(序列化消息,类型)

位图

用户365天的签到记录,统计月活
get/set设置整个位图的内容
getbit/setbit设置位数组的内容

HyperLogLog

统计每天的PV:设置计数器,incryby
统计每天的UV:set集合存储用户id,scard
pfadd用法和sadd用法一致
pfcount用法和scard用法一致
pfmerge将多个计数值累加

布隆过滤器

实质是一个不精确的set结构,只会误判那些没见过的元素
推介内容去重
bf.add添加元素
bf.exists查询元素是否存在
bf.madd添加多个元素
bf.mexists查询多个元素是否存在

简单限流-滑动窗口

规定某行为在规定时间的次数
使用zset结构记录用户行为历史,通过score圈出这个时间窗口。通过统计滑动窗口内的行为数量与阈值进行比较,就可判断当前行为是否允许。
key表示同一用户同一种行为,value记录时间戳(无意义),score记录行为发生的时间
算法思路:
首先会记录当前行为的时间到score,然后移除窗口外的行为记录,然后获取窗口内的行为数,判断其是否超出阈值

漏斗限流

redis是单线程,所以无需考虑并发问题。

大海捞针scan

怎么从海量key中找出满足特定前缀的key列表来??

keys reg缺点

  1. 没有offset,limit参数,一次性输出所有满足条件的key
  2. keys算法是遍历算法,0(N)复杂度,若实例中有千万级以上的key,这个指令会导致redis其他指令延后甚至超时报错,因为redis是单线程程序,顺序执行所有指令

scan原理

通过游标分步进行的,不会阻塞线程
控制返回最大条数,提供limit参数

scan特点

返回的结果可能会重复,需要客户端去重
遍历结束的标志是返回的游标值为0

scan使用

scan 游标值 key正则 单词遍历个数
scan 0 math key99* count 1000
zscan遍历zset有序集合元素
hscan遍历hash字典元素
sscan遍历set集合元素

分析:

  1. redis所有key都存储在一个很大的字典中,scan的游标就是hash表的索引值
  2. 因为哈希表数组的长度为2的n次方,所以取模运算 == 位与运算
    a mod 8=a&(8-1)
    amod 16=a&(16-1)
  3. 哈希表一次性重哈希,会将旧数组迁移到新数组,会造成卡顿现象
    采用渐进式rehash同时保留旧数组和新数组,在定时任务中以及后续操作中渐渐的进行迁移。
    3