> For the complete documentation index, see [llms.txt](https://shniu.gitbook.io/cs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://shniu.gitbook.io/cs/system-design/backend-store/cache-design.md).

# 缓存设计

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

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

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](/cs/middleware/redis.md)
* 书籍：[深入分布式缓存：从原理到实践](https://weread.qq.com/web/reader/98a323b05e0b5e98afa107ckc81322c012c81e728d9d180)
