缓存是什么
最初人们使用缓存只是为了提高 CPU 的利用率,因为 CPU 的计算速度远远超过内存的读写速度。为了减少 CPU 等待内存的时间,工程师在 CPU 和内存之间引入了速度更快的内存。内存分为 DRAM 和 SRAM 两种, DRAM 的读写速度比 SRAM 慢但价格比 SRAM 低,SRAM 价格昂贵但读写速度很快。DRAM 用来做普通内存条,SRAM 用来做 CPU 的高速缓存。
如今缓存的概念已被延伸,不仅在 CPU 和内存之间有缓存,而且在内存和硬盘之间也有缓存,乃至在硬盘与网络之间也有网络内容缓存。凡是位于速度相差较大的两种硬件之间,用于协调两者数据传输速度差异的结构,均可称之为缓存。
缓存在互联网中的适用场景
性能要求高的场景:比如为了提高网站的访问速度,使用 CDN 来缓存网页。
数据实时性要求不高的场景:比如用缓存来保存社交网络中的点赞数、评论数等。
使用缓存带来的问题
数据不一致
数据并不是一成不变的,只要数据保存在多个地方,就要考虑到数据不一致的问题。
解决方案:
缓存是通过牺牲强一致性来提高性能的,在软件设计上,我们基本上不可能做出一个没有缺陷的设计,就像算法设计中的时间换空间、空间换时间一个道理,有时候强一致性和高性能是有冲突的,我们应该在高性能和强一致性之间做取舍。
缓存雪崩
缓存雪崩是指缓存服务因为意外发生了宕机或者因为大量的 key 被设置了相同的过期时间,导致缓存在同一时刻失效,大量请求的流量都直接落在数据库上。此时,如果没有先处理这个故障,而是先重启数据库的话,数据库依然会直面大量流量的冲击。
解决方案:
- 提前做好缓存服务高可用保障,如持久化、主从复制、哨兵和集群。
- 提前做好业务层的服务限流、服务降级。
缓存穿透
缓存穿透一般是指来自黑客的恶意攻击,黑客发过来的 key 有很多都不在缓存中,使得数据库直面流量冲击。
解决方案:
- 每次只要在数据库中没有查询到数据,就写一个空值到缓存中,并且设置一个过期时间。
缓存击穿
缓存击穿是指某个访问量非常大的 key 在失效的瞬间,大量的请求直接查询数据库。
解决方案:
- 若缓存的数据不需要更新,可以将该数据设置为永不过期。
- 若缓存的数据更新频繁或者缓存刷新的流程耗时较长,可以用定时线程在缓存过期前重新构建缓存或者延后缓存的过期时间。
- 若缓存的数据更新不频繁且缓存刷新的流程耗时较短,可以采用基于 Redis、Zookeeper 等分布式中间件的分布式互斥锁,或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存,其余请求在锁释放后访问新的缓存。
缓存更新模式
在处理多个并发请求时,有人会用以下两种做法来处理数据:
先更新数据库再更新缓存, 这种做法可能会出现一种情况: 数据库先更新的请求反而后更新缓存,数据库后更新的请求反而先更新缓存。这样就会造成数据库和缓存中的数据不一致,应用程序中读取的是脏数据。
先删除缓存再更新数据库,这种做法可能会出现一种情况:更新数据库的请求先删除了缓存,此时正好有一个并发的读请求在没有命中缓存后从数据库中取出旧的数据并且更新到缓存中,这个时候更新数据库的请求也完成了数据库更新。此时,数据库和缓存中的数据不一致, 应用程序中读取的是脏数据。
上面两种做法都不正确,以下几种缓存更新模式才是我们需要的。
Cache Aside 模式
先更新数据库再删除缓存,这种做法就是 Cache Aside 模式, 一种简单易用的模式,在实际的应用中也推荐使用这种模式,虽然这种模式理论上还是存在问题,但实际上出现的概率比较低。 这种做法可能会出现这样一种情况:一个读取数据的请求没有命中缓存,然后就到数据库中取数据,此时来了一个写入数据的请求,写完数据库后让缓存失效,然后之前的那个读请求再把旧的数据放进去, 应用程序中读取的是脏数据。
Read/Write Through 模式
在 Cache Aside 模式中,应用程序需要维护两个数据源,缓存和数据库。而在Read/Write Through 模式中,应用程序只需要维护缓存,数据库的维护工作由缓存代理。
Read Through
Read Through 是指在缓存中读取不到数据时,缓存服务去数据库加载数据并更新到缓存中。
Write Through
Write Through 是当有数据更新的时候,如果缓存中有旧的数据就先更新缓存,然后缓存服务去更新数据库;如果缓存中没有旧的数据,那么缓存服务直接去更新数据库就好了。
Write Behind Caching 模式
Write Behind Caching 模式是 Read/Write Through 模式 的一个变种,区别就是 Read/Write Through 模式的缓存写数据库的时候是同步的,而 Write Behind 模式 的缓存操作数据库是异步的,Write Behind Caching 也叫 Write Back。
Write Behind Caching 模式的具体流程是,应用程序在更新数据的时候,只更新缓存,不更新数据库,缓存服务会异步地批量更新数据库。这个设计的好处就是直接操作内存速度快,而且因为是异步的,所以缓存服务还可以合并对同一个数据的多次操作到数据库,进一步提升效率。
Write Behind Caching 模式的缺点是数据不是强一致性的,还可能会有数据丢失的情况,实现逻辑也较为复杂。