相关链接
Reids缓存穿透、击穿、雪崩
Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,它也带来了一些问题。其中,最主要的就是数据的一致性问题,从严格意义上讲,如果对于数据的一致性要求很高,那么就不能使用缓存。
缓存穿透(查不到)
用户想要查询一个数据,发现redis内存数据中没有(缓存未命中),于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中(大量查询不存在的key),于是都去请求了持久层数据库。这会给持久层的数据库造成很大的压力,这时候就相当于出现了缓存穿透。
解决方案1:布隆过滤器 (Bloom Filter)
简介:布隆过滤器可以用于检索一个元素是否在一个集合中。在用户请求后,先在控制层通过布隆过滤器判断请求的key是否存在。存在则进入Redis获取key,不存在则丢弃。
原理:它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。
优点:1.空间效率和查询时间都远远超过一般的算法。2.缓存空间小
缺点:1.有一定的误识别率和删除困难。2.代码维护困难
解决方案2:Redis缓存空值
简介:在用户请求后且Redis缓存未命中后,将数据库中的空值也缓存到缓存层中,这样查询该空值就不会再访问DB,而是直接在缓存层访问就行。
原理:数据库结果反馈给Redis,避免下次再请求数据库。
优点:1.实现方式简单,不需要额外加层。2.代码易于维护
缺点:1.数据不一致。2. 需要过多的存储空间。
缓存击穿(缓存过期,量太大)
如果出现上述情况,采用了Redis缓存空值的方法来解决缓存穿透,那么还有可能会出现缓存击穿问题。当某一个key非常热点(微博热搜),通过缓存空值其放入了Redis中,在这个key失效的一瞬间(在下次重新缓存该key的空值之前,有个很短时间的空隙),持续的大量并发就穿破缓存(大量查询同一个key),直接请求数据库,导致数据库瞬间压力过大。
解决方案1:设置热点数据永不过期
简介:从缓存层面来看,没有设置过期时间,所以不会出现热点key过期后产生的问题。
优点:简单暴力。
缺点:Redis空间会越来越大,很快达到上限。
解决方案2:加互斥锁 (mutex key)
简介:分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其它线程没有获得分布式锁的权限,因此只需要等待该线程将数据放入缓存,直接从缓存中获取即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。
原理:setnx(set if not exist) 不存在再设置key(在分布式锁中,会经常使用)
优点:
缺点:
缓存雪崩(缓存集体过期)
指在某一个时间段,缓存集体过期失效(周期性的波峰压力),导致大量请求直接进入到数据库。
产生雪崩的场景:双十一零点抢购,这波商品比较集中的放入缓存,假设缓存设置为一小时,那么到了凌晨一点钟的时候,这一批商品的缓存都过期了。而对于这批商品再次的访问查询,都会进入到数据库中,对于数据库而言,就会产生周期性的压力波峰。当波峰超出存储层性能瓶颈,就会导致存储层挂掉。
缓存雪崩最致命的问题在于,缓存服务器某个节点宕机或断网。因为自然形成的空值缓存失效,无非就是对数据库产生周期性的压力。二服务器缓存节点宕机,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。
解决方案1:redis高可用
简介:多增设几台redis,这样一台挂掉之后其他的还可以继续工作。
原理:搭建redis分片集群。
优点:redis几乎不会宕机。
缺点:需要额外成本。
解决方案2:服务降级
简介:停掉一些服务,保证主要的服务可用。(双十一当天不可退款)
原理:减少不重要的服务,回收JVM的内存与CPU资源。
优点:节约成本。
缺点:部分服务不可用。
解决方案3:限流降级
简介:缓存失效后,通过加锁或者队列来控制读数据库和写缓存的线程数量。
原理:互斥锁 -> 对某个key只允许一个线程查询数据和写缓存,其他线程等待从缓存获取。
缺点:可能造成死锁。
解决方案4:数据预热
简介:在正式部署前,把可能的数据先预先访问一遍,这样一些可能大量访问的数据就会加载到缓存中,在即将发生大并发访问前,手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间尽量均匀。
22/03/15
M