# 缓存设计

> 缓存，是一种存储数据的组件，它的作用是让对数据的请求更快地返回。实际上，凡是位于速度相差较大的两种硬件之间，用于协调两者数据传输速度差异的结构，均可称之为缓存。

在做缓存设计时，有一些误区要避开：

1. 把缓存作为服务间传递数据的通道，这个要避免
2. 使用缓存没有考虑雪崩的情况，这个要考虑
3. 多服务共享一个缓存实例，这个最好要避免，多个服务之间缓存做垂直拆分

常见缓存的应用场景

缓存和缓冲区的比较

缓存的分类

### 进程内缓存

缓存可以放在进程外，如redis/memcached，也可以放在进程内，如Guava Cache、加锁的Map、Leveldb等，就叫做进程内缓存（一般是缓存架构设计中的二级缓存或者三级缓存）

进程内缓存可以缓存多种多样的数据类型，如对象、JSON数据、文本数据、HTML等等，它带来的直接好处是不需要再次访问后端存储来获取数据，可以提高查询效率，相较于进程外缓存，一是节省了内部带宽，二是响应延时会更低。

进程内缓存虽然有很多好处（节省带宽、响应延时更低等），但是它更存在一些不可逾越的问题：

服务实例一般是多实例部署的，每个实例都有自己的进程内缓存，怎么保持实时一致性？这样就把状态带进了服务中，违反了分层架构中的服务无状态设计，为水平扩展带来了一些隐患。（服务无状态设计的原则：**尽量把数据的状态和存储放在专门的数据存储服务中，实例节点本身只做计算，不把数据状态和某个实例做耦合，这样在水平扩展时才能收放自如**）那什么时候适合用进程内缓存呢？

1. 只读数据。这类数据在整个服务生命周期中不发生变化，就可以预先把数据加载放在内存中
2. 允许数据短暂不一致的业务场景。比如一些运营页面、某些场景的计数等

所以，进程内缓存要慎用，大部分情况下，进程外缓存基本就可以搞定了；真的要使用进程内缓存时，一定要想好是否需要实时的缓存数据一致性？缓存怎么在多实例之间做更新？业务场景是否允许数据不一致等等，其他可以参考[这里](https://mp.weixin.qq.com/s/Car6EkaNzaJ7gaFa2EZxOw)

### 缓存淘汰还是修改

参考：[缓存，是淘汰还是修改](https://mp.weixin.qq.com/s/YBpOz1dQ0sG15vGL7N0PeQ)

结论：通用做法，缓存要选择淘汰，而不是修改；特殊场景，缓存可以修改。

在大部分场景中，缓存修改的代价比缓存淘汰要大的多。

### 资源

* 有关 Redis 做缓存请参考 [中间件/Redis](https://shniu.gitbook.io/cs/middleware/redis)
* 书籍：[深入分布式缓存：从原理到实践](https://weread.qq.com/web/reader/98a323b05e0b5e98afa107ckc81322c012c81e728d9d180)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://shniu.gitbook.io/cs/system-design/backend-store/cache-design.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
