<返回更多

分布式多级缓存系统设计与实战

2023-05-05  政采云技术  
加入收藏

1. 缓存系统概述

 

图片

如上图,是一次最基本的网络请求。用户请求从界面(浏览器或 App 界面)到网络转发、应用服务再到存储(数据库或文件系统),然后返回到界面呈现内容。

 

随着互联网的普及,内容信息越来越复杂,用户数和访问量越来越大,我们的应用需要支撑更多的并发量,同时我们的应用服务器和数据库服务器所做的计算也越来越多。但是往往我们的应用服务器资源是有限的,数据库每秒能接受的请求次数也是有限的。如何能够有效利用有限的资源来提供尽可能大的吞吐量?是每个开发同学绕不开的课题。一个有效的办法就是引入缓存,打破标准流程,如下图1到4每个环节中请求可以从缓存中直接获取目标数据并返回,从而减少计算量,有效提升响应速度,让有限的资源服务更多的用户。

图片

image.png

缓存可以应用上图1到4个的各个环节中,且不同环节缓存策略略有不同。本文将主要从3和4点讲解缓存的使用。

2. 缓存架构演变

2.1. 无缓存架构

 

图片

如上图,是一次最基本的网络请求。请求从网络层直接请求到 DB。此时请求耗时最大卡点在数据库的磁盘 IO 上。

 

2.2. 引入分布式缓存数据库

 

针对2.1无缓存架构的数据库磁盘IO耗时,可添加了一道缓存数据库例如 redis。借助缓存中间件,可消除数据库的 IO 瓶颈。快速返回数据。如下图:

图片

通过缓存数据库1、可防止流量直接打到数据库层,减缓数据库压力。2、缓存快速返回,可提高请求查询速率。

 

2.2.1 为什么选择redis?

此时系统卡点在缓存数据库的网络通信上。即使缓存数据库读取数据很快,但是和应用服务间仍然隔着一层网络通信。

2.3. 引入 JVM 本地缓存

 

针对2.2缓存数据库架构,访问缓存数据库的网络通信问题,可在 JVM 应用层添加本地缓存,解决网络 IO 问题。如下图

图片

 

在应用内部新增本地缓存,使流量在应用层直接返回。避免进一步访问到 redis。

本架构虽然可大大提高数据读取速率,但其成本也是更高的。

建议根据自身业务场景,从以下3方面考量是否才有本地缓存。

常用本地缓存

2.3.1 数据读取流程

图片

按优先级依次从本地、redis、DB 中读取数据。实现了本地(一级缓存)、缓存数据库(二级缓存)和 DB 的多级缓存架构。

3. 痛点和优化

3.1 数据一致性问题

 

存在多级缓存,虽然大大提高了数据的读取速率。但是数据散落在各个不同的区域,数据一致性就是一个绕不过去的问题。特别是针对本地缓存,同时散落在多个多台 JVM 实例中。数据变更时,必须同步修改redis、本地缓存和DB。以下是基于canal + 广播消息实现的一致性异步处理方案。

图片

 

3.1.1 同步缓存机制

优点:操作简单

缺点:未命中缓存时,取重新加载。此次查询请求慢。

**注意:**此方案同步缓存,为先 DB 操作、后异步同步缓存。会存在短暂 DB 和缓存不一致场景。需根据自身业务场景考量,如有必要,可前置删除缓存,再 DB 操作。

3.2. 热点 key 监控

 

以上架构,系统缓存只能被动加载。只有 key 被访问后,系统才能触发加载。在高并发的情况下,如一直出现缓存穿透,大量流量请求到数据库,对数据库还是很大的考验。所以优秀的缓存系统,应该能自动识别出热点 key。前置将数据缓存下来。

图片

 

3.2.1 热点 key 探测

引入缓存中间调度服务:热点 key 探测中间服务器概念

详情见:参考

4. 缓存注意事项

4.1 key 设计

4.1.1 value 设计

redis 是单线程机制,big key 会阻塞后续请求。

仅缓存必要的字段,不必要字段,及时瘦身

4.1.2 缓存穿透

访问一个不存在的 key。由于实际上并不存在,所以每次都会访 DB

4.1.3 缓存击穿

某个 key 瞬间访问量过大,但突然过期,导致大部分流量打到了 DB

4.1.4 缓存雪崩

由于大部分 key 设置了相同的失效时间,某一时间大量缓存同时失效,导致大部分流量瞬间打到 DB,导致 DB 压力过大。

5. 实战经验

6. 踩坑记录

6.1. 本地缓存被污染

由于缓存在 JVM 内部,且保存在老年代。业务方拿去使用的时候,直接修改了缓存的数据,导致缓存数据不正确。

取对象时,直接 copy 一份。(复制对象耗 CPU,不推荐)

将缓存对象设置成不可编辑。(推荐)

6.2. 缓存计算结果,而不是响应结果

缓存的 value 是 Response 对象,首次请求失败,导致缓存的数据为response.success=false。后续所有命中均操作失败。

6.3. 本地内存彪高,触发频繁 full GC

初次引入本地缓存(之前是 redis )。将大量数据缓存在本地,导致 JVM 内存彪高。

引入本地缓存前考虑预计内存,进而考虑是否值得接入本地缓存。

仅缓存热点 key,非热点 key 不缓存在本地

6.4. 降级到 redis 缓存,CPU 彪高

为优化 JVM 内存,将本地缓存降级到 redis。QPS 高场景,触发大量序列化和young GC,导致系统 CPU 彪高。

评估 QPS,考虑是否可降级

仅缓存热点 key,非热点 key 不缓存在本地

7. 总结

在计算机世界里,缓存无处不在。但不管缓存系统如何设计,其本质都是空间换时间。也就是提升数据的获取速率。

缓存系统的设计各有千秋、各有优劣。没有最优秀的架构,只有最适合的架构。应该根据自身实际业务情况考虑缓存架构的设计。并从缓存命中率、数据库压力、数据一致性、系统吞吐量等综合评估设计的合理性。

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>