一、概述
redis作为一种基于键值对的非关系型数据库,由于其直接在内存中进行读写,具有特别高效的读写效率,所以常用于作为数据库的缓存,当服务器需要查询数据时,先去redis中查询,如果查询命中,则直接返回数据给服务器;如果查询未命中,再去数据库中进行查询。redis中缓存数据库中的热点数据,可以在高并发的情况下,有效减小数据库查询的压力。
虽然redis能有效提升查询效率和并发性,但是也存在一定的问题,典型的问题即:
- 缓存穿透
- 缓存击穿
- 缓存雪崩
二、缓存穿透
定义:当用户查询一个数据,发现redis内存数据库中没有,即缓存没有命中,于是向持久层数据库查询,发现持久层数据库也没有,于是本次查询失败。当用户量很多的时候,缓存都未命中,大量的请求会冲击持久层数据库,对持久层数据库造成很大压力,即缓存穿透现象。
解决方案:
1、缓存空对象
当存储层未命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据,将会从缓存中获取,保护了底层数据库。因此即便发生缓存穿透,也只会发生一次查询未命中,以后都会从redis中查询命中并返回空值。
2、布隆过滤器
布隆过滤器本质上是一种数据结构,对数据库中存在的数据建立一个hash映射表,当需要查询某数据时,先查询布隆过滤器中是否存在该数据的映射,如果存在,再对数据库进行查询,如果不存在,则直接丢弃该查询。相比Set、List、Map等数据结构,其内存占用更小,更高效,但缺点是返回的结果是不确切的,即“某样东西可能存在和一定不存在”。
布隆过滤器是一种二进制向量数组,数组中元素的初始值为0,当需要映射一个值到布隆过滤器中时,通过k个哈希函数对该值进行哈希计算,并将每个哈希值对应的二进制数组位置的值变为1。这样当我们要查询某个值是否映射到了布隆过滤器中时,由k个哈希函数计算得到k个哈希值,再查询数组中对应的k个位置的二进制值是否为1,如果都为1,则该值有可能在映射在布隆过滤器中(哈希值的计算会有重复性,类似于hashmap的哈希冲突);只要存在一个位置的二进制值为0,则表示布隆过滤器中一定没有要查询的值的映射。
三、缓存击穿
定义:大并发集中对一个热点key进行访问,当这个key在缓存失效的瞬间,持续的大并发就穿破缓存,直接请求数据库。
解决方案:
1、设置热点数据永不过期。(并不是很好的方法)
2、加互斥锁。保证对每个key同时只有一个线程去查询数据库,其他线程没有获得分布式锁的权限,则进行等待。
四、缓存雪崩
定义:当redis中缓存的热点数据集中过期失效,或是redis宕机,导致大量查询指向持久化数据库,引起数据库压力过大甚至宕机。
解决方案:
1、建立redis集群,一个redis宕机不影响全局。
2、对热点数据的过期时间间隔设置,让缓存失效的时间点尽量均匀。
3、通过加锁或者队列来控制读数据库写缓存的线程数量。