Su的技术博客

  • 首页
  • 原创
  • 视频
  • Java
  • MySQL
  • DDD
  • 事故复盘
  • 架构方案
  • AI
  • Other
  • 工具
    • AI工具集
    • 工具清单
    • JSON在线格式化
    • JSON在线比较
    • SQL在线格式化
  • 打赏
  • 关于
路很长,又很短
  1. 首页
  2. 架构方案
  3. 正文
                           

【转载】系统设计 | 应用系统缓存

2024-01-16 1414点热度 0人点赞 0条评论

系统设计 | 应用系统缓存


在应用系统中,使用缓存不算非常难的事,但是设计好一套缓存策略比较麻烦,这样既能起到好的缓存效果也能在合适的时候更新缓存。

今天我们聊聊缓存。

不过,在计算机科学中,有很多种缓存。我们先聊聊缓存的类型,再谈应用系统中的缓存。

我们会涉及哪些缓存知识?

这里我把缓存分为两类:

  • 非应用缓存:难以被程序员干预、控制和使用的缓存,但是我们能通过选择合适的数据结构,写出缓存友好的代码。
  • 应用缓存:容易被程序员干预、控制和使用的缓存。

非应用缓存有:

  • CPU 缓存。例如,使用数组的 index 取数会比指针取数更快,相关指令更容易被 CPU 缓存。
  • 磁盘缓存。尽量通过流的方式来读取磁盘文件,另外也应该避免不必要的 flush 操作。
  • 数据库缓存。数据库常见的缓存有 SQL 缓存和查询缓存,前者只是缓存 SQL 的解析结果,后者会把结果集也缓存下来,一般需要手动开启。
  • ORM 缓存。ORM 会在同一会话中对多次查询进行缓存。
  • 网关缓存或者服务商缓存。例如,Nginx 等网络服务器缓存,经常会出现一些问题。
  • DNS 缓存。DNS 解析也会被缓存下来。
  • CDN 缓存。通过网络服务商城域网对静态资源进行缓存。

非应用缓存通常不会侵入业务,比较透明,我们需要知道它们存在必要时做出配置。另外,对程序员来说更多的关心应用缓存,这些缓存往往和业务相关。

应用缓存有:

  • 内存本地缓存。我们可以使用 WeakHashMap、Guava、Caffeine、EVcache 等方法直接缓存到内存。甚至直接放到对象静态属性上作为单例值。违反认知的地方是,很多地方应该大量使用本地缓存,避免一上来直接使用 Redis 缓存。它的适用场景有:不需要多个实例保持一致性,缓存数据量大,不需要外部失效,需要高速存取;单次请求或者上下文中的数据。
  • 分布式缓存。分布式缓存使用的场景是需要多个实例保持一致性,且规模比较大避免占用应用服务的内存。常见的技术方案有:Redis、Memcached、Tair等。
  • 计算缓存。除了对数据进行缓存外,还可以对计算结果进行缓存,用了节省 CPU 时间。例如递归的记忆化。
  • 前端缓存。利用设置 HTTP 头信息将数据缓存到浏览器上。

通常来说,分布式缓存需要一套有效的缓存策略。回答:缓存哪种类型的对象?缓存的颗粒度如何?什么时候去更新?

缓存对象颗粒度

对于后端服务来说,根据分层会有不同的 POJO(API 返回对象、领域对象、数据库 PO),我们缓存什么呢?实际项目中这几种情况都会有。

为了取得最好的缓存效果(命中率高,手动失效少),需要权衡被缓存对象的颗粒度。

缓存 API 返回对象(Response)

如果以 Response 为粒度,其实是以用例为视角。比如订单详情,需要组装非常多的数据,且变化不剧烈。

特点是:

  • 颗粒度大,缓存的效果好(纳入缓存的内容多,包括数据和计算逻辑)。
  • 命中概率低,组成 key 的条件太多。
  • 更新策略不好控制,开发难度比较大,需要加很多代码,需要看业务是否能接受,举个例子:用户详情,使用用户 ID 做 Key,相关地方都需要手动刷新,例如地址、积分、消费记录等。
  • 部分电商公司使用该方案,互联网场中景,用户基数比较大,并发请求高,取数代价高。
  • 缓存 key 一般是 URL 中关键路径信息。

缓存领域对象聚合

如果使用 DDD 分层,有聚合概念,可以以聚合粒度缓存。

其特点是:

  • 颗粒度适中,缓存的效果适中。
  • 根据聚合根来控制缓存失效。
  • 部分数据可能不会被纳入缓存,因为组装为 Response 的过程不会被缓存。
  • 依赖 DDD 的取数逻辑,有时候为整存整取。
  • 缓存 key 一般是聚合根 ID。

缓存数据库 PO(Persistent Object)

如果使用了 Spring Data JPA 本质上 PO 在内部被框架实现了,可以自动开启,并按照约定更新缓存。

如果使用 Mybatis、Mybatis Plus 一般会定义自己的 PO 对象,所以可以单独处理缓存策略。

它的特点是:

  • 颗粒度小,缓存的效果小。
  • 可以借助 ORM 框架缓存,Session 内多次获取,可以避免再查询,需要开启二级缓存。
  • 缓存 key 一般是表的主键。

一般来说,缓存颗粒度越小,失效策略越好处理,但是缓存住的数据和逻辑就越少,在有些场景下不能满足我们的期望。

另外,还有一些特殊场景的缓存。

  • 列表页查询缓存。因为很难触发更新策略,一般不加缓存,直接走读库 CQRS 模式,或者走 ES,推荐 ES 的更新策略为 COW(Copy On Write)。在条件业务允许时,也可以根据查询条件做很短过期的查询。
  • 大 key。大 key 存在反而会导致性能瓶颈,业界 10k 以上会被叫做大 key,需要对 key 进行拆分缓存即可。
  • 写缓存。一般不对写进行缓存,但是一些高并发的场景,会将写数据放入 Redis 异步写入,也可以看做一种缓存。

一般来说:推荐使用聚合缓存;列表不缓存,使用读写分离从库查询;更新均不做缓存,只对单个查缓存。

缓存设计注意事项

缓存雪崩、缓存击穿、缓存穿透

缓存雪崩是指在某一个时刻突然缓存都失效了。原因有两种,一种是缓存服务器宕机了,流量全部进入数据库;另外一种情况是在同一时刻失效了。

对于前者可以通过熔断、高可用等设计,而后者需要对缓存过期时间加一个随机偏移值,避免同时失效。

缓存击穿和雪崩有点类似,业界往往说的是系统健康运行依赖某些热点 key 的缓存,当这些热点 key 失效后流量全部打到数据库上。

为了保证热点 key 安全,在一些关键系统甚至会用多套 Redis 分级处理,或者将其设置为永不过期,通过程序触发更新的方式保证服务可用性。这种思想有点以前 CMS 站点的静态化,将动态页面输出为 HTML 页面静态化。

缓存穿透常常说的是,明明有 Redis 缓存但是偏偏大部分都不能命中进入到数据库。有时候是因为 key 设计不合理,导致命中率很低,其它情况有可能是遇到爬虫或者攻击,制造了大量的无效参数,这些参数不会命中缓存直接进入了数据库查询阶段。

如果频繁发生缓存穿透,刚好条件又合适可以采用返回空对象,避免回源到数据库。

缓存更新策略

我们一般不会主动更新缓存,而是让其失效,在下一次取数时如果没有缓存则更新缓存。

缓存更新在不同场景下有几种策略:

  • 自然过期:通过时间作为自然过期策略。
  • 主动失效:进程内可以通过注解实现,在合适的更新场景触发相关缓存失效;进程间可以通过 MQ 实现封装一个分布式的失效注解。
  • 主动预热:使用脚本,在服务上线后跑一遍热点数据进行预热。

需要缓存的常见场景

  • 热点数据:用户信息、权限数据、配置表或者元数据。
  • 高价值数据:目录树、机构树、DashBoard 统计值。
  • 大 I/O 数据:前端缓存。

序列化和反序列化坑

  • 不推荐使用 Java 自带的序列化。
  • 推荐将对象序列化为 JSON,但是不要使用带类型信息的格式,否则在包调整后反序列化会失败。
  • 上线后最好清掉缓存,重新预热,否则会出现各种反序列化问题。

如何写出方便缓存的代码?

缓存友好的代码,其本质是容易找到一个标识标记这组数据,这也是为什么列表页不适合缓存的原因。

  • 命令和查询分离,对状态更新的操作和返回数据结果的操作不要使用同一个方法。
  • 少用魔法,尽量不使用 IOP 自动填充值或者组装数据
  • 尽量使用参数表传参,少用对象传参,这样方便找到缓存 key

参考资料

  • Java演示CPU级的缓存效果 https://blog.csdn.net/JavaMonsterr/article/details/125147238
  • 什么是缓存雪崩、缓存击穿、缓存穿透?https://zhuanlan.zhihu.com/p/346651831
  • Extension to the DDD skeleton project: caching in the service layer https://dotnetcodr.com/2014/03/24/extension-to-the-ddd-skeleton-project-caching-in-the-service-layer/
  • 8.10.3 The MySQL Query Cache https://dev.mysql.com/doc/refman/5.7/en/query-cache.html
  • Buffer cache: What is it and how does it impact database performance? https://blog.quest.com/buffer-cache-what-is-it-and-how-does-it-impact-database-performance/
  • Webinar 笔记 http://shaogefenhao.com/libs/webinar-notes/java-solution-webinar-25.html

文 | 少个分号 (转载请注明出处)

 

本文仅供学习!所有权归属原作者。侵删!文章来源: DDD和微服务 -shaogefenhao :http://mp.weixin.qq.com/s?__biz=MzA4Mzc2MzcyMQ==&mid=2247484648&idx=1&sn=afa5b64ba0d76fd92d931f52065a7607&chksm=9ff0338ea887ba98985cf262c864f4140f5b428c5956f86d5baa33816a612af7f8b37e7dd25d&scene=21#wechat_redirect

更多文章:

  1. 京东平台研发朱志国:领域驱动设计(DDD)理论启示
  2. 系统设计入门:成为高级软件工程师的指南
  3. 从MVC到DDD,该如何下手重构?
  4. 一次 Redis 事务使用不当引发的生产事故
  5. Eureka源码剖析之三:服务拉取
  6. 基于DDD的微服务设计和开发实战
  7. 随机高并发查询结果一致性设计实践
  8. 手把手教你落地DDD
  9. 浅析Redis大Key
  10. 系统设计 | 如何表达技术架构?(规划篇)
标签: 转载 架构 Java 缓存 缓存击穿 cache 系统设计 缓存穿透 缓存雪崩
最后更新:2024-01-16

coder

分享干货文章,学习先进经验。

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

广告
文章目录
  • 我们会涉及哪些缓存知识?
  • 缓存对象颗粒度
  • 缓存设计注意事项
  • 参考资料
最新 热点 推荐
最新 热点 推荐
干货 | 论Elasticsearch数据建模的重要性 马蜂窝消息总线——面向业务的消息服务设计 基于 MySQL Binlog 实现可配置的异构数据同步 视频笔记:Google发布Agent2Agent协议 视频笔记:什么是微服务,为什么是微服务? 视频笔记:什么是AI 智能体? 视频笔记:什么是Flink? 如何秒级实现接口间“幂等”补偿:一款轻量级仿幂等数据校正处理辅助工具
Elasticsearch 使用误区之六——富文本内容写入前不清洗基于 MySQL Binlog 实现可配置的异构数据同步马蜂窝消息总线——面向业务的消息服务设计干货 | 论Elasticsearch数据建模的重要性你可以不用RxJava,但必须得领悟它的思想!如何秒级实现接口间“幂等”补偿:一款轻量级仿幂等数据校正处理辅助工具视频笔记:什么是Flink?视频笔记:什么是AI 智能体?
什么是Event Sourcing?跟CQRS和DDD有什么关系 富途证券关于2021.10.9凌晨交易中断事故的道歉和回复 Elasticsearch 中 _count 和 _stats 文档数量不一致的困惑与解决方案 做好技术负责人的4个关键特质 笔记 | Java对象探秘 图解Git 基于DDD的微服务设计和开发实战 如何设计一款高性能分布式锁,实现数据的安全访问?

CRUD (1) Event Sourcing (1) graphql (1) id (1) NoSQL (1) quarkus (1) rest (1) RocketMQ (2) Spring Boot (1) zk (1) zookeeper (1) 上下文 (1) 事务消息 (1) 二级缓存 (1) 值对象 (1) 关系数据库 (1) 分布式缓存 (1) 原子性 (1) 唯一ID (1) 商品 (1) 多对多 (1) 子域 (1) 字符集 (1) 客户端心跳 (1) 幂等 (2) 干货 (1) 并发 (1) 应用场景 (1) 应用架构图 (1) 康威定律 (2) 异步复制 (1) 微服务架构 (2) 总体方案 (1) 技术方案 (2) 技术架构 (2) 技术架构图 (1) 技能 (1) 持续集成 (1) 支撑域 (1) 故障恢复 (1) 数据架构图 (1) 方案选型 (1) 日记 (1) 服务发现 (1) 服务治理 (1) 服务注册 (2) 机房 (1) 核心域 (1) 泄漏 (1) 洋葱架构 (1) 消息队列 (5) 源码剖析 (1) 灰度发布 (1) 熔断 (1) 生态 (1) 画图工具 (1) 研发团队 (1) 线程 (2) 组织架构 (1) 缓存架构 (1) 编码 (1) 视频 (18) 读写分离 (1) 贵州 (1) 软件设计 (1) 迁移 (1) 通用域 (1) 集群化 (1) 雪花算法 (1) 顺序消息 (1)

推荐链接🔗
  • AI工具集
  • 工具箱🛠️

COPYRIGHT © 2014-2025 verysu.com . ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

粤ICP备15033072号-2

x