# Cache 缓存三问

哎，我一个个人项目基本上都没有超过过两位数的QPS的人，面试你天天问我缓存的这些问题，**你们礼貌吗？**

# 缓存穿透
## 是啥
当请求试图访问一个在缓存和数据库都不存在的 key时，因为缓存没有，所以请求会直接转发到数据库上。然而数据库里面也没有这个 key 的记录，所以也就没有办法将这个数据写入缓存。当下一次同样的请求来了的时候，还是会转发到数据库上。

在这种情况下，缓存基本上也就没用了，就像被穿透了一样，请求每次都会走到数据库，流量大时数据库可能会扛不住。

## 咋整
1. 对请求进行校验。一些明显不合理的参数直接前端过滤掉就好了，就不要让他进来了。
2. 将空值缓存起来。查完之后，即使数据库没有这条记录，也把这个 key 和没有记录的情况缓存起来，这样下次就不会再查数据库了。
3. 布隆过滤器。挺有意思的一个东西，值得单独讲讲。

### 布隆过滤器
#### 介绍
布隆过滤器是一种概率型的数据结构，它可以告诉你某条记录一定不存在或者有可能存在。

正常我们考虑一个东西是否存在的判断的时候，大家下意识的反应可能会是哈希。但是哈希需要保存所有的对象，以便后续哈希冲突的时候找到特定的值。所以这也导致了哈希对大量数据的时候，内存是非常大的。

所以就有个小天才，尝试我们用多个哈希，来讲存在变成一个概率性的事情：

比如说：
- 我们初始化一个值为0，容量为10的数组：[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
然后设定两个不同的哈希方法：
- 哈希1 => %5
- 哈希2 => %7

现在，我们要存放 25 这个值，我们来看一下：
25：哈希1得到0，我们将0这个下标对应的值标记成1；哈希2得到4，我们将4这个下标对应的值修改成1；此时数组为：[1, 0, 0, 0, 1, 0, 0, 0, 0, 0]

当我们尝试获取25的时候，我们还是按照哈希1和哈希2，取得对应的下标，然后获取下标对应的值。如果下标的值一旦有0值，那么我们就认为这条记录是不存在的，但即便是都是1，也不一定百分百存在，为什么呢？

假设说我们现在要存 60 这个值，哈希1返回下标0，哈希2返回下标4，跟25是一样的结果。所以0和4的下标都是1的时候，可能是25带来的，也可以能是60带来的，这并不能确定，只能说，有概率存在。

但这个种并不是很精准筛查，在某些条件下，已经可以帮我们解决很大程度上的问题了。

#### 哈希函数个数和布隆过滤器长度
布隆过滤器长度越长，误报概率越小。
哈希函数越多，一个值写的1也越多。

#### 经典应用
用布隆过滤器减少磁盘 IO 和网络请求。

# 缓存击穿
## 是啥
某一热门的 key，在它过期的一瞬间，突然来了对它的好多请求。因为缓存已经过期了，所以这些请求就都转到了数据库，那个可怜的数据库又扛不住了。

## 咋整
1. 加互斥锁。比如 Redis 分布式锁。
2. 设置热点数据不过期。

# 缓存雪崩
## 是啥
大量热点 key 设置了相同的过期时间，在他们过期的一瞬间，出现了大量的请求，然后那个数据库应该更扛不住了。可以简单的理解，缓存击穿时一个 key，缓存雪崩是一堆 key。

## 咋整
基本上跟缓存击穿一样，无法时加了一个打散过期时间来处理一组的问题。
1. 打散过期时间
2. 加互斥锁
3. 热点数据不过期
