这篇文章我们来介绍一些Redis中常见的问题以及它们的解决方案。
一、缓存雪崩
缓存雪崩指的是大量的不同key同一时间全部失效,所有请求都打到数据库中,导致数据库宕机。
解决方案:
- 在设置key的过期时间时加入随机数,使得key不在同一时间过期,给足系统同步缓存的时间。
- 使用分布式锁等锁机制,控制整个系统访问数据库的并发量。
- 热点数据设置不过期。
二、缓存击穿
缓存层被击穿,缓存中无访问的key,而数据库中有。这是缓存中key过期导致的。
解决方案:
- 基于分布式锁以及其他锁机制,控制访问数据库的并发量
- 热点key设置不过期。
三、缓存穿透
缓存层和数据库层都无访问的key,整个系统被穿透。
解决方案:
- 布隆过滤器,防止不存在的数据访问整个系统
- 缓存空值。
四、Redis中的大Key问题
在Redis中数据都是以k-v存储的,一般k只是起到索引标识的作用,所以不会很大,在Redis中的大Key一般指的是value过于庞大。
问题:
在前文中我们知道,Redis处理数据是单线程的,这样没有多线程的切换,让redis变得很快,但是如果k-v中的v过大,就会增加读取和处理的时间,从而阻塞整个线程,导致整个redis服务的访问性能下降。
解决方案:
对value进行拆分,把一个k-v合理的拆分成多个k-v存储。
五、缓存和数据库一致性问题
在高并发的系统中,由于存在着多线程操作,会导致缓存中的数据和数据库中的数据不一致。一般有三种方案来解决数据不一致的情况,缓存延迟双删、异步监听数据库binlog删除 + 重试、旁路缓存。 这里只介绍业务中经常使用的旁路缓存模式。
具体操作:
读操作:去缓存中读取数据,如果存在则返回;如果数据不存在,去数据库中读取数据,更新到缓存中,并返回。
写操作:更新数据库,删除缓存。
写操作更新和删除的顺序问题:
1.先删除缓存,再更新数据库,此时可能会出现如下操作。
当前数据情况:数据库 x =a , 缓存:x = a ; 线程1执行写操作第一步,先删除缓存。
当前数据情况:数据库 x =a , 缓存:空 ; 线程2执行读操作,此时缓存为空,读取数据库数据,更新缓存,并返回。
当前数据情况:数据库 x =a , 缓存:x = a ; 线程1执行写操作第二步,更新数据库 x = b。
当前数据情况:数据库 x =b , 缓存:x = a ; 线程1,2,3执行读操作,此时缓存与数据库数据已经不一致。
2.先更新数据库,再删除缓存,此时可能会出现如下操作。
当前数据情况:数据库 x =a , 缓存:x = 空 ; 线程1执行读操作,缓存不存在,去数据库中读取数据 x = a。
当前数据情况:数据库 x =a , 缓存:x = 空 ; 线程2执行写操作,更新数据库x = b , 并删除缓存。
当前数据情况:数据库 x =b, 缓存:x = 空 ; 线程1执行读操作第二步,更新缓存 x = a 。
当前数据情况:数据库 x =b , 缓存:x = a ; 线程1,2,3执行读操作,此时缓存与数据库数据已经不一致。
为什么采用方案2呢。因为操作数据库的时间比操作缓存的时间慢很多。当执行完数据库操作,再执行缓存操作,可粗略认为是同一时间,方案2的不一致基本不会出现。