当前位置: 首页 > news >正文

在项目中如何巧妙使用缓存

缓存

对于经常访问的数据,每次都从数据库(硬盘)中获取是比较慢,可以利用性能更高的存储来提高系统响应速度,俗称缓存 。合理使用缓存可以显著降低数据库的压力、提高系统性能。
那么,什么样的数据适合缓存呢?一般情况下就4个字“读多写少 ”,要频繁查询的、不怎么修改的。
具体来说:

  1. 高频访问的数据:如系统首页、热门推荐内容等。
  2. 计算成本较高的数据:如复杂查询结果、大量数据的统计结果。
  3. 允许短时间延迟的数据:如不需要实时更新的排行榜、图片列表等。
    在我们的项目中,主页是用户高频访问的内容,调用的获取图片列表的接口也是高频访问的。而且即使数据更新存在一定延迟,也不会对用户体验造成明显影响,因此非常适合缓存。

Redis分布式缓存

分布式缓存是指将缓存数据分布存储在多台服务器上,以便在高并发场景下提供更高的吞吐量和更好的容错性。
Redis是实现分布式缓存的主流方案,也是后端开发必学的技能。主要是由于它具有下面几个优势:

  • 高性能:基于内存操作,访问速度极快。单节点 Redis的读写QPS可达10w次每秒!
  • 丰富的数据结构:支持字符串、列表、集合、哈希、位图等,适用于各种数据结构存储。
  • 分布式支持:可以通过RedisCluster构建高可用、高性能的分布式缓存,还提供哨兵集群机制提升可用性、提供分片集群机制提高可扩展性。

缓存设计

需要缓存首页的图片列表数据,也就是对 listPictureVOByPage接口进行缓存。首先按照缓存3要素"key、value、过期时间”进行设计。

  1. 缓存 key 设计
    由于接口支持传入不同的查询条件,对应的数据不同,因此需要将查询条件作为缓存key的一部分。
    可以将查询条件对象转换为JSON字符串,但这个JSON会比较长,可以利用哈希算法(md5)来压缩key。
    此外,由于使用分布式缓存,可能由多个项目和业务共享,因此需要在key的开头拼接前缀进行隔离。设计出的key如下:
    yunpicture:listPictureVOByPage:${查询条件key}
  2. 缓存 value 设计
    缓存从数据库中查到的Page分页对象,存储为什么格式呢?这里有2种选择:
  • 为了可读性,可以转换为JSON结构的字符串
  • 为了压缩空间,可以存为二进制等其他结构
    但是对应的 Redis 数据结构都是 string。
  1. 缓存过期时间设置
    必须设置缓存过期时间! 根据实际业务场景和缓存空间的大小、数据的一致性的要求设置,合适即可,此处由于查询条件较多、而且考虑到图片会持续更新,设置为5~60分钟即可。

Caffeine 本地缓存

当应用需要频繁访问某些数据时,可以将这些数据缓存到应用的内存中(比如JVM中);下次访问时,直接从内存读取,而不需要经过网络或其他存储系统。
相比于分布式缓存,本地缓存的速度更快,但是无法在多个服务器间共享数据、而且不方便扩容。
所以本地缓存的应用场景一般是:

  • 数据访问量有限的小型数据集
  • 不需要服务器间共享数据的单机应用
  • 高频、低延迟的访问场景(如用户临时会话信息、短期热点数据)。
    对于Java项目,Caffeine是主流的本地缓存技术,拥有极高的性能和丰富的功能。比如可以精确控制缓存数量和大小、支持缓存过期、支持多种缓存淘汰策略、支持异步操作、线程安全等。
    由于本地缓存不需要引入额外的中间件,成本更低。因此如果只是要提升数据访问性能,优先考虑本地缓存而不是分布式缓存。

缓存设计

本地缓存的设计和分布式缓存基本一致,不再赞述。但有2个区别:

  1. 本地缓存需要自己创建初始化缓存结构(可以简单理解为要自己new一个HashMap)。
  2. 由于本地缓存本身就是服务器隔离的,而且占用服务器的内存,key可以更精简一些,不用再添加项目前缀。

多级缓存

多级缓存是指结合本地缓存和分布式缓存的优点,在同一业务场景下构建两级缓存系统,这样可以兼顾本地缓存的高性能、以及分布式缓存的数据一致性和可靠性。
多级缓存的工作流程:

  1. 第一级(Caffeine本地缓存):优先从本地缓存中读取数据。如果命中,则直接返回。
  2. 第二级(Redis分布式缓存):如果本地缓存未命中,则查询Redis分布式缓存。如果Redis命中,则返回数据并更新本地缓存。
  3. 数据库查询:如果Redis也未命中,则查询数据库,并将结果写入Redis和本地缓存。
http://www.lryc.cn/news/576419.html

相关文章:

  • SQL 子查询全位置解析:可编写子查询的 7 大子句
  • Flutter基础(路由页面跳转)
  • 布林带的使用
  • 展开说说:Android之ContentProvider源码浅析
  • 【数据结构】map/set模拟实现(红黑树作底层)
  • STM32HAL 旋转编码器教程
  • 1.2 基于蜂鸟E203处理器的完整开发流程
  • Tailwind CSS工作原理
  • 从 “慢如蜗牛” 到 “风驰电掣”:中欧跨境网络专线加速方案
  • 电子电气架构 --- 车载芯片SOC简介
  • 深入解析 Electron 架构:主进程 vs 渲染进程
  • 软测八股--计算机网络
  • AD 学习笔记——第一章 系统的安装及参数设置
  • 鸿蒙HarmonyOS 5小游戏实践:数字记忆挑战(附:源代码)
  • ZooKeeper深度面试指南二
  • Flutter基础(Children|​​Actions|Container|decoration|child)
  • STM32F103之SPI软件读写W25Q64
  • 力扣第73题-矩阵置零
  • 用鸿蒙打造真正的跨设备数据库:从零实现分布式存储
  • 【区块链】区块链交易(Transaction)之nonce
  • 默克树技术原理
  • Node.js特训专栏-实战进阶:10.MongoDB文档操作与聚合框架
  • 嵌入式硬件与应用篇---寄存器GPIO控制
  • 软件反调试(1)- 基于进程列表的检测
  • Spring AI Alibaba 入门指南:打造企业级 AI 应用
  • 《从 0 到 1 掌握正则表达式:解析串口数据的万能钥匙》
  • Note2.3 机器学习:Adaptive Learning Rate
  • golang中struct中大小写对gin框架的影响
  • 深入剖析AI大模型:Dify的介绍
  • SpringMVC系列(七)(Restful架构风格(下))(完结篇)