一、缓存击穿
1、什么叫缓存击穿
系统中某个查询次数很多的热点key,在某个时刻过期,而此时又正好有大量并发请求查询这个key,但是缓存的重建还没有完成,这样,就会有大量请求涌向后端数据库,使得其压力骤增甚至崩溃
2、如何解决缓存击穿
- 使用分布式互斥锁:只允许一个线程获取到锁,由它去查询数据库,然后将查询到的数据设置到缓存中,其他线程必须等待重建缓存的线程执行完,然后去缓存中获取数据
- 设置永不过期
- 在设置热点key的时候,不给key设置过期时间
- 或者正常给key设置过期时间,不过在后台同时启一个定时任务去定时地更新这个缓存
二、缓存穿透
1、什么叫缓存穿透
缓存穿透的意思就是,应用查询一个不存在的数据,在缓存中查不到,去数据库查也查不到,这样就无法写入缓存。如果出现大量此类请求,每次查询都访问数据库,就会导致后端数据库压力骤增,缓存失去了对数据库的保护作用
2、为什么会发生缓存穿透
- 自身的业务代码出了问题,如set和get的key不一致,导致查不到数据
- 遭到恶意的网络攻击
3、如何解决缓存穿透
- 缓存空对象:将数据库返回的null缓存起来,设置一个较短的过期时间
- 使用布隆过滤器,可以结合博主之前写的bitmap理解
- 简单来说就是在初始化布隆过滤器时,会先将所有key进行n次hash运算,这样就可以得到n个位置,然后将这n个位置上的元素改为1
- 然后请求过来后,我们对要查询的key做hash运算得到某个位置,然后看布隆过滤器中对应位置元素的值是否为1
- 如果对应位置元素的值为1,就证明key在库中存在,则继续向下查询
- 如果对应位置元素的值不为1,那么就证明key在库中不存在,直接返回客户端空即可
- 使用黑白名单:如果是恶意攻击,我们可以通过只允许某些IP访问我们的服务或者禁止某些IP访问我们的服务
三、缓存雪崩
1、什么叫缓存雪崩
缓存中有大量的key在同一时刻过期,或者Redis直接宕机了,导致大量的查询请求全部到达数据库,造成数据库查询压力骤增,甚至直接挂掉
2、如何解决缓存雪崩
- 将每个key的过期时间打散,使它们的失效点尽可能均匀分布
- 针对一些常用的数据设置成为永久有效
- 使用高可用集群,防止redis宕机造成缓存不可用
四、分布式锁
1、什么是分布式锁
所有服务中的所有线程都去获取同一把锁,但只有一个线程可以成功的获得锁,其他没有获得锁的线程必须全部等待,直到持有锁的线程释放锁。在集群场景下,使用分布式锁,有助于保证数据的一致性
2、实现分布式锁的方案
- 基于数据库实现
- 基于缓存(Redis等,基于redis实现性能最高)
- 基于zookeeper(可靠性最高)
3、使用setnx设置分布式锁
- 使用
setnx key value
去设置数据,使用del key
去删除数据
这个命令的意思就是,如果key存在,就无法更改value
所以不管是集群中哪个节点想修改这个key,在使用del之前都无法成功 - 然后为了防止设置key之后,节点宕机无法执行
del key
操作导致这个key一直被锁定,我们还需要设置过期时间,所以我们执行完setnx key value
之后使用expire key <time>
命令给这个key设置过期时间 - 还有一点需要考虑的是,如果在执行
expire key <time>
命令之前,节点宕机,那么这个key还是会被锁定,所以我们要考虑原子性操作,使用set key value nx ex <expireTime>
命令来完成设置key和过期时间的操作(注意,此指令只能限制setnx命令,限制不了set命令)
127.0.0.1:6379> set k1 10 nx ex 60
OK
127.0.0.1:6379>
127.0.0.1:6379> setnx k1 20
(integer) 0
127.0.0.1:6379> ttl k1
(integer) 47
127.0.0.1:6379>
127.0.0.1:6379> set k1 20
OK
127.0.0.1:6379> get k1
"20"
127.0.0.1:6379>
4、通过uuid解决锁误删的情况
在多线程情况下,我们还可能会遇到锁被误删的问题
为了解决这种误删问题,我们在执行set key value nx ex <expireTime>
命令时,value可以带上uuid作为区分其他线程的依据,业务代码在执行释放锁操作之前,先去判断获取到的value是否符合或包含自己的那个uuid,这样就避免了因为卡顿会误删别人的锁的情况
5、使用LUA保证删除操作原子性
由上图可知,在某些极端场景下,还是会出现误删他人锁的情况
所以我们的解决方法就是,将第三步中的判断和释放操作写在一段LUA脚本中,然后excute执行,以此来实现释放操作的原子性
五、redis6.0后的新特性
- redis6.0之后新增了ACL,有兴趣可以自行百度
- redis-cli支持cluster(5.0及以前想搭建集群还要安装ruby环境)
- 另外支持网络数据读写以及协议解析的多线程(命令执行还是单线程)
如有错误,欢迎指正!!!