1.性能因素影响

Redis的主要性能瓶颈是内存或者网络带宽而不是CPU。

2.Redis是单线程还是多线程?

redis6工作线程是单线程的,整个是多线程的。

多个IO线程解决网络IO问题

单个工作线程保证线程安全

3.五种经典数据类型及场景

3.1 string

使用场景:

  1. 点赞 incrby key
  2. 分布式锁 setnx key value

3.2 hash (Map<string,Map<object,object>>)

使用场景:

  1. 购物车

3.3 list

使用场景:

  1. 微信公众号订阅消息
  2. 某个商品的评论列表

3.4 set

使用场景:

  1. 微信抽奖小程序 srandmember key 元素不删除,spop key 元素删除
  2. 微信朋友圈点赞
  3. 微博共同关注
  4. 微博我关注的人也关注的他(爱好相同)
  5. QQ可能认识的人

3.5 zset

使用场景:

  1. 抖音热搜

4.新数据类型

4.1 bitmap: 由0和1状态表现的二进制位的bit数组

使用场景:

  1. 日活统计
  2. 近一周的活跃用户
  3. 连续签到打卡
  4. 某用户按照一年365天,哪几天登陆过?哪几天没有登陆?全年中登录的天数共计多少?

4.2 hyperloglog: 去重复统计功能的基数估计法

HyperLogLog的标准错误为1.04/ sqrt(m),其中是使用的寄存器数,Redis使用16384个寄存器,因此标准误差为0.81%

1
2
3
4
UV(unique visitor): 用户访问次数,需要去重
PV(page view): 页面访问次数
DAU(daily active user): 日活跃用户量,需去重
MAU(monthly active user): 月活跃用户量,需去重

使用场景:

  1. 统计某个网站或某个文章的uv
  2. 用户搜索网站关键词的数量
  3. 统计用户每天的搜索不同词条个数

4.3 GEO

使用场景:

  1. 附近的酒店

5.布隆过滤器BloomFilter

布隆过滤器是一种类似set的数据结构,只是统计结果不太准确,本质就是判断具体数据存不存在一个大的集合中

一个元素如果判断结果为存在的时候元素不一定存在, 但是判断结果为不存在的时候则一定不存在。

优点:高效地插入和查询,占用空间少

缺点:

  1. 不能删除元素。 因为删掉元素会导致误判率增加,因为hash冲突同一个位置可能存的东西是多个共有的, 你删除一个元素的同时可能也把其它的删除了。
  2. 存在误判,不同的数据可能出来相同的hash值

使用场景:

  1. 解决缓存穿透问题
  2. 黑名单校验

6. 缓存雪崩,缓存穿透,缓存击穿

6.1 缓存雪崩

问题:缓存中有大量数据同时过期,或redis主机挂了,Redis 全盘崩溃

解决:

  1. redis缓存集群实现高可用
  2. ehcache本地缓存 + Hystrix或者阿里sentinel限流&降级
  3. 开启Redis持久化机制aof/rdb,尽快恢复缓存集群

6.2 缓存穿透

问题:请求去查询一条记录,先redis后mysql发现都查询不到该条记录, 但是请求每次都会打到数据库上面去,导致后台数据库压力暴增,

解决:

  1. 空对象缓存或者缺省值,但可以使用不同id恶意攻击
  2. Google布隆过滤器Guava,重大的缺陷就是只能单机使用
  3. Redis布隆过滤器

6.2 缓存击穿

问题:大量的请求同时查询一个 key 时, 此时这个key正好失效了,就会导致大量的请求都打到数据库上面去

解决:

  1. 对于访问频繁的热点key,干脆就不设置过期时间
  2. 互斥独占锁防止击穿
  3. Redis布隆过滤器
  4. 双缓存,互斥更新,差异失效时间(开辟两块缓存,主A从B,先查A,A没有再查B,先更新B再更新A,B的过期时间比A略长)

lua脚本可以解决原子性问题,但不能解决高并发问题

7.分布式锁

7.1一个靠谱分布式锁需要具备的条件和刚需

1.独占性 OnlyOne,任何时刻只能有且仅有一个线程持有

2.高可用 若redis集群环境下,不能因为某一个节点挂了而出现获取锁和释放锁失败的情况

3.防死锁 杜绝死锁,必须有超时控制机制或者撤销操作,有个兜底终止跳出方案

4.不乱抢 防止张冠李戴,不能私下unlock别人的锁,只能自己加锁自己释放。

5.重入性 同一个节点的同一个线程如果获得锁之后,它也可以再次获取这个锁。

7.2 一步步优化卖票案例

1.单机版没加锁,出现超卖现象

加synchronized 或 ReentrantLock

2.分布式部署后,单机锁还是出现超卖现象,需要分布式锁

redis分布式锁setnx(key,value)

3.出现异常锁资源没有释放

必须在finally代码块释放锁

4.服务宕机,部署了微服务jar包的机器挂了,代码层面根本没有走到finally这块, 没办法保证解锁

锁加上过期时间

5.分开设置key+过期时间,没有原子性,设置过期时间前宕机

1行代码同时设置key和过期时间

6.业务执行时间大于锁的过期时间,之前的请求删除了后来请求的锁

判断value,相同才删除

7.finally中判断和删除不是原子操作

lua脚本保证原子性

8.redis集群AP,redis异步复制造成的锁丢失?

比如:主节点没来的及把刚刚set进来这条数据给从节点,master就挂了,从机上位但从机上无该数据

Redisson

1
2
3
4
RLock lock = redissonClient.getLock(key);
if(lock.isLocked() && lock.isHeldByCurrentThread()){ // 是锁定状态 且 是当前执行线程的锁
lock.unlock(); // 释放锁
}

9.如何确保锁的过期时间大于业务执行时间,redis分布式锁如何续期?

Redisson看门狗机制

8.RedLock

使用场景:多个服务间保证同一时刻同一时间段内同一用户只能有一个请求(防止关键业务出现并发攻击)

单机:如果redis发生故障,分布式锁不可用

集群:1主多从,线程1将键值对写入master后,(由于redis集群AP,异步复制)如果master发生了故障,slave 升级为新的 master;
此时新的 master 并不包含线程 1 写入的键值对,因此线程 2 尝试获取锁也可以成功拿到锁;
此时相当于有两个线程获取到了锁,可能会导致各种预期之外的情况发生,例如最常见的脏数据。
我们加的是排它独占锁,同一时间只能有一个建redis锁成功并持有锁,严禁出现2个以上的请求线程拿到锁。

解决方案,舍弃slave,异步复制,使用多master,不是主从,N(部署台数) = 2X(宕机数) + 1

多个master中同时存在同一把锁

参考文档

Redis 命令参考