# Cache

## 使用场景

### 高性能

若从数据库查询（复杂查询、跨表跨库查询）一个数据比较耗时，而且数据在短时间内不会变，那么可以把查询结果做缓存。

由于缓存是内存，所以性能大大由于数据库。

### 高并发

## 缺点

### 缓存与数据库双写不一致

Cache Aside Pattern：

* 读，先读缓存，再读数据库。
* 更新，先**删除**缓存，再更新数据库。选择删除而不是跟新缓存的原因：很多时候是经过复杂查询、计算后的结果，更新的话成本太大。其实使用的是 lazy 的思想。

#### 情况一

**场景**：先修改数据库，再删除缓存，如果删除缓存失败了，那么会导致数据库中是新数据，缓存中是旧数据，数据出现不一致。

**解决方案**：先删除缓存，再修改数据库。

#### 情况二

**场景**：先删除缓存，然后去修改数据库，此时还没修改完成，一个请求过来，去读缓存，发现缓存空了，去查询数据库，查到了修改前的旧数据，放到了缓存中，此时数据变更的程序完成了数据库的修改。

解决方案：

### 缓存雪崩

缓存挂了，所有请求瞬时全部到数据库，导致数据库宕机。

解决方案：

* 保证缓存高可用。
* 系统内缓存，比如 ehcache。
* 在请求数据库层做限流+降级。比如 Hystrix。
* 缓存持久化，可以尽快恢复缓存。

### 缓存穿透

由于不正确的请求（比如黑客攻击），所有的请求都命中不到缓存，导致全部请求到数据库。比如数据库中 id 为 1 -100，发出请求 id 为 -1。

可以把数据库查不到的数据写一个空值到缓存里面去。

### 缓存并发竞争

**场景**：多个服务同时对缓存中的某个 key 做更新，没有按照期望的顺序执行，所以最终的缓存数据不正确。

**解决方案**：分布式锁+时间戳。

![](https://3232244687-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYZow-MmROshIrkwdtE%2F-La5sKcQ14gGfLWQbxV3%2F-La8YT16KNnhcb1dXE60%2F01_redis%E5%B9%B6%E5%8F%91%E7%AB%9E%E4%BA%89%E9%97%AE%E9%A2%98%E4%BB%A5%E5%8F%8A%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88.png?alt=media\&token=f14d4281-d198-4acc-ae76-5668583bb465)
