缓存击穿
作为缓存,受到内存大小限制,可能:
- key 超过了过期时间
- key 被 LRU LFU 清掉了
因为某些 key 不在 redis 里面了,大量并发来找这个 key 的时候,这时候客户端去直接请求数据库,这就是击穿。
这个问题怎么解决?
只要发现某个key不存在,就让所有对这个key的请求去抢一把锁。也就是说,
让第一个找key的请求,执行一个setnx,类似于放一把锁。只有获得锁的人才能去数据库查,其他的请求让它们失败,sleep等待几秒钟之后,重新去 redis 取数据。
这会存在一个问题:
1、如果第一个拿到锁的人挂了,别人也拿不到锁,这样就死锁了。可以设置锁的过期时间来避免这个问题。
2、由于我设置了过期时间,可能会发生这样的情况:拿到锁的人没挂,但是可能由于网络拥塞或者数据库拥塞,锁超时了,又有一个人拿到这个锁,又去数据库取,更加拥塞了。
针对这个问题,可以开启多个线程,一个线程去库里取数据,另一个线程去给锁的超时时间延长。这样会让代码逻辑变得复杂。
3、像上面这样,你自己去实现分布式协调很麻烦。因此我们引入Zokeeper,这个以后再讲~
缓存穿透
从业务接收查询的,是你系统里面根本不存在的数据。这就是缓存穿透。
怎么解决?
1、使用布隆过滤器
- 你可以在客户端中包含布隆过滤器的算法
- 你可以在客户端只包含算法,在redis中存放bitmap
- 你可以直接在redis中集成布隆模块:RedisBloom模块
布隆过滤器的缺点:只能增加,不能删除,如果你的业务删除了数据库中的某条数据,无法在布隆过滤器中删除这个key
解决方式:你可以使用布谷鸟过滤器等其他支持删除操作的过滤器,或者设置一个空 key
缓存雪崩
和击穿有些类似,都是后面有数据的情况。
大量 key 同时失效,间接造成大量的访问到达 DB
怎么解决?
要考虑两种情况:
1、每天都要更新数据的情况,例如每天零点要刷新缓存。这时候可以依赖击穿的解决方案。或者在业务层加一个小延时:判断如果是零点就延时,随机sleep几秒,这样不会把流量一大波流量同时放过来。
对于能够提前预知的时点数据,比如京东双11的页面样式、图片等,可以提前推到客户端本地,到双11零点的时候直接切换即可。
2、与时点性无关(并不需要在某个时间刷新缓存)的话,可以设置随机过期时间。
Redis做分布式锁
1、setnx
2、过期时间
3、多线程(守护线程)延长过期时间
可以使用j ava Sedisson API
也可以用 zookeeper 做分布式锁,这样是最容易的。虽然zookeeper没有redis快,但是比redis能够加强准确性。
API
redis 支持大多数语言,因此它二进制安全保证了数据在跨语言的时候不会出问题
可以查看所有配置项
spring redis 文档