什么是缓存击穿 ? 怎么解决 ?
缓存击穿的意思是对于设置了过期时间的
key
,缓存在某个时间点过期的时
候,恰好这时间点对这个
Key
有大量的并发请求过来,这些请求发现缓存过
期一般都会从后端
DB
加载数据并回设到缓存,这个时候大并发的请求可能
会瞬间把
DB
压垮。
解决方案有两种方式:
第一可以使用互斥锁:当缓存失效时,不立即去
load db
,先使用如
Redis
的
setnx
去设置一个互斥锁,当操作成功返回时再进行
load db
的操作并回设缓
存,否则重试
get
缓存的方法
第二种方案可以设置当前
key
逻辑过期,大概是思路如下:
①
:在设置
key
的时候,设置一个过期时间字段一块存入缓存中,不给当前
key
设置过期时间
②
:当查询的时候,从
redis
取出数据后判断时间是否过期
③
:如果过期则开通另外一个线程进行数据同步,当前线程正常返回数据,
这个数据不是最新
当然两种方案各有利弊:
如果选择数据的强一致性,建议使用分布式锁的方案,性能上可能没那么
高,锁需要等,也有可能产生死锁的问题
如果选择
key
的逻辑删除,则优先考虑的高可用性,性能比较高,但是数据
同步这块做不到强一致。
什么是缓存雪崩 ? 怎么解决 ?
缓存雪崩意思是设置缓存时采用了相同的过期时间,导致缓存在某一时刻同
时失效,请求全部转发到
DB
,
DB
瞬时压力过重雪崩。与缓存击穿的区别:
雪崩是很多
key
,击穿是某一个
key
缓存。
解决方案主要是可以将缓存失效时间分散开,比如可以在原有的失效时间基
础上增加一个随机值,比如
1-5
分钟随机,这样每一个缓存的过期时间的重
复率就会降低,就很难引发集体失效的事件。
redis做为缓存,mysql的数据如何与redis进行同步呢?(双写一致
性)
就说我最近做的这个项目,里面有
xxxx
(
根据自己的简历上 写)的功能,
需要让数据库与
redis
高度保持一致,因为要求时效性比较高,
我们当时采用的读写锁保证的强一致性。
我们采用的是
redisson
实现的读写锁,在读的时候添加共享锁,可以保证读
读不互斥,读写互斥。当我们更新数据的时候,添加排他锁,它是读写,读
读都互斥,这样就能保证在写数据的同时是不会让其他线程读数据的,避免
了脏数据。这里面需要注意的是读方法和写方法上需要使用同一把锁才行。
那这个排他锁是如何保证读写、读读互斥的呢?
其实排他锁底层使用也是
setnx
,保证了同时只能有一个线程操作
锁住的方法
听说过延时双删吗?为什么不用它呢?
延迟双删,如果是写操作,我们先把缓存中的数据删除,然后更新
数据库,最后再延时删除缓存中的数据,其中这个延时多久不太好确定,在
延时的过程中可能会出现脏数据,并不能保证强一致性,所以没有采用它。
当时设置的
allkeys-lru
策略。把最近最常访问的
数据留在缓存中。
Redis分布式锁如何实现 ?
在
redis
中提供了一个命令
setnx(SET if not exists)
由于
redis
的单线程的,用了命令之后,只能有一个客户端对某一个
key
设置
值,在没有过期或删除
key
的时候是其他客户端是不能设置这个
key
的
如何控制Redis实现分布式锁有效时长呢?
redis
的
setnx
指令不好控制这个问题,我们当时采用的
redis
的一个框架
redisson
实现的。
在
redisson
中需要手动加锁,并且可以控制锁的失效时间和等待时间,当锁
住的一个业务还没有执行完成的时候,在
redisson
中引入了一个看门狗机
制,就是说每隔一段时间就检查当前业务是否还持有锁,如果持有就增加加
锁的持有时间,当业务执行完成之后需要使用释放锁就可以了
还有一个好处就是,在高并发下,一个业务有可能会执行很快,先客户
1
持
有锁的时候,客户
2
来了以后并不会马上拒绝,它会自旋不断尝试获取锁,
如果客户
1
释放之后,客户
2
就可以马上持有锁,性能也得到了提升。