关于系统数据缓存的思考以及设计

文章目录

  • 引言
  • 案例
    • A项目
    • B项目
  • 分析
  • 我的实现
  • 总结

引言

缓存,这是一个经久不衰的话题,它通过“空间换时间”的战术不仅能够极大提升处理查询性能还能很好的保护底层资源。最近针对系统数据缓存的优化后,由于这是一个通用的场景并且有了一点心得因此在这里分享出来。

案例

无论是传统Web后端应用还是大数据平台服务应用,本质上都是一个Java进程并且应用相关数据一般都是存在Mysql。从抽象的角度来看,所有请求基本都要经过以下几个流程:参数校验、鉴权、请求处理、请求响应。这几个流程特别是前几个往往需要依赖存在MySQL中的系统数据,例如判断请求中携带的的服务ID在系统中是否有对应的服务、获取服务对应的认证信息进行身份校验、请求处理时常常依赖的一些数据如某个变动非常低频的小表或者一些配置在MySQL中的系统配置等,此时如果是你,会怎么去设计的这个缓存?我先举两个负责过的项目之前是如何设计的

A项目

服务启动的时候读取MySQL中的系统数据并以HashMap的格式存在Java进程中,不会自动更新,在有配置变动时循环调用所有服务器的ip接口触发重新加载数据。具体流程如下
在这里插入图片描述

这种设计的优点如下

  1. 实现简单,不依赖外部组件
  2. 有数据变更立马更新所有缓存数据,基本不会在缓存中读到旧数据的逻辑

以上是这种设计的优点,可能也是设计者这么设计的原因。但是随着维护会出现以下问题

  1. 所有服务器的ip是配置在apollo中的,因此每次扩容机器时都要将新的ip增加到apollo中,否则可能会出现缓存不更新现象
  2. 在服务从ECS迁移到K8s后,服务的ip是动态分配的并且每次重启后都不一样,因此当有配置变动时只能手动重启服务或者执行curl调用接口来触发各个节点进行配置更新

B项目

服务启动的时候读取MySQL中的系统数据并以HashMap的格式存在Java进程中,并记录当前更新时间,在查询缓存时会判断此时距离上次更新时间是否已经超过30秒(硬编码),如果超过则重新触发查询MySQL更新缓存。具体流程如下
在这里插入图片描述

这种设计的优点如下

  1. 服务之间无需通信,相比A服务来说是会自己主动更新

除了上面这一个优点貌似旧没有了,值得吐槽的点很多,大致列举一下它实现的问题

  1. 针对每一个需要更新的缓存都新建一个自己独立的类来实现CommandLineRunner接口
  2. 硬编码30s过期的逻辑以及查询时再去做更新动作(类似第一次惩罚)
  3. 更新缓存时的逻辑是,先全量查询Mysql对应的表,然后循环对比缓存中的数据,有新增的就追加到缓存,再去跟缓存比较是否有删除的,有的话再去缓存中删除

分析

这两个服务在更新系统数据缓存上都存在不少问题,现在再回过头来思考🤔下,我们理想的系统数据缓存的实现应该是什么样子的?我大概整理了一下大致如下

  1. 服务启动时要全量读取对应的数据以Map格式存在Java进程的内存中
  2. 服务应该是定时自行去Mysql同步最新的数据,并且定时周期是允许可配置的
  3. 一些重要配置变更时触发各个服务立马去Mysql同步数据(可选,需要参考具体业务场景)

基于这个场景我对比了下当前比较热门的缓存工具Guava和Caffeine,最终发现它们都不适合咱们的这个场景。它们的设计更偏向于解决缓存热点数据的问题,简单来说就是咱们的场景是Java内存有10G,而存在Mysql中的数据有100M,我们需要将这100M的数据存在Java内存中进行请求加速,需要解决的是如何全量加载到内存的问题。而Guava和Caffeine的设计要解决的问题时,如何更有效的合理的利用内存,例如Java内存只有10G,而存在MySQL的数据有100G,因此需要将这100G中的热点数据存在Java内存中来提升性能和降低高频访问Mysql,它具体要考虑的问题大致有以下几点

  1. 通过LRU算法或者变种来保留热点数据
  2. 通过大小限制以及时间限制来剔除掉缓存数据从而保证Java内存不会被撑爆
  3. 在有新数据写入或者数据更新时,同步更新缓存中的数据

通过这些你会发现其实这个场景真不适合用Guava和Caffeine,这个过程中也翻阅了一些大佬的实现,但是发现都有点跑偏了,有种为了用Caffeine而强行用的味道。例如 https://www.vincentli.top/2020/09/01/jvm-local-cache-case-caffeine/,这里本质上还是跟B项目的实现一样,只不过用Caffeine替换了Java的HashMap罢了。在这里补充说下,Caffeine是非常优秀的缓存工具,现在很多优秀的开源组件例如 Pulsar 中也会用它来加速查询,但盲目的使用是不可取的,并且这肯定也不是Caffeine设计者想看到的。

我的实现

上面说了这么多,我分享下自己的实现供大家参考以及批判。在这里通过一个定时线程池实现即可,在启动时触发一次并在后续周期性的刷新配置即可,核心代码也就两三行。先看下流程
在这里插入图片描述

代码实现如下

public class CacheManager implements CommandLineRunner {

    private static final Long DEFAULT_INTERVAL = 36000L;

    private final ScheduledExecutorService apiSystemConfigExecutorService =  Executors.newSingleThreadScheduledExecutor();

    private static Map<String, String> apiSysConfig = new HashMap<>();

    @Override
    public void run(String... args) throws Exception {
        apiSystemConfigExecutorService.scheduleAtFixedRate(this::loadApiSysConfigDataFromDB,
                0, Optional.ofNullable(PropertyConfigurer.getLong("SYSTEM_CONFIG_UPDATE_PERIOD")).orElse(DEFAULT_INTERVAL), TimeUnit.SECONDS);
    }

    public void loadApiSysConfigDataFromDB(){
        ApisDao apisDao = SpringContextHolder.getBean(ApisDao.class);
        List<ApiSysConfigEntity> sysConfigEntityList = apisDao.selectSystemConfig();

        if (null == sysConfigEntityList || sysConfigEntityList.size() == 0){
            return;
        }

        logger.info("load ApiSysConfigEntity from api_sys_config in mysql ,the size of api in cache  is " + sysConfigEntityList.size());
        apiSysConfig = sysConfigEntityList.stream()
                .collect(Collectors.toMap(ApiSysConfigEntity::getName, ApiSysConfigEntity::getValue));
    }
}

总结

在工作中只要我们观察和思考,就会发现其实是存在不少值得完善的地方,此时应该考虑对它们进行完善,否则如果长期维护一个“丑陋”的系统,你的思维、以及审美也会随之跟着降低,以至于久而久之就觉得这种设计也挺好的,甚至后续再有类似的场景时你还是会选择这种设计。工作的本质也是一场修行,在对系统进行改进完善的过程也是自我完善的过程,简称“借物得道”,同时如果读者针对这个场景有更合适的设计也欢迎在下方一起讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/552055.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣练习题(2024/4/15)

1打家劫舍 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表每个房屋…

java实现论文查重,文本查重方案 采用 ansj 分词法

需求要求实现一个文本查重&#xff0c;重复率超过70% 就不让用户新增文本。固研究实现基于java的文本查重工具&#xff0c;分享出来方便大家使用&#xff5e; ansj 分词法介绍 Ansj 是一个开源的 Java 中文分词工具&#xff0c;基于中科院的 ictclas 中文分词算法&#xff0c…

THS6.0.1.0开启健康检查(by lqw)

可以在节点管理器或者分组管理的编辑配置里添加以下信息&#xff1a; 之后点监控,点击实时指标&#xff0c;点击HTTP集群统计&#xff1a; 下图是配置并生效的效果&#xff1a; 也可以使用頁面配置&#xff1a; 推荐使用tcp形式&#xff0c;有的应用后端可能不支持http…

代理知识科普:为什么有的代理IP速度比较慢呢?

代理IP在跨境业务中被广泛的应用&#xff0c;今天我们将一同深入探讨一个问题&#xff1a;“为什么有的IP代理速度比较慢&#xff1f;”随着数字化时代的不断发展&#xff0c;代理服务成为了许多网络操作的关键环节。然而&#xff0c;有时我们可能会遇到IP代理速度慢的问题&…

MT3026 砍玉米

样例1&#xff1a; 输入&#xff1a; 6 1 3 4 2 5 1 7 8 19 10 30 2 输出&#xff1a; 6 其中1<n<10^5,1<xi,hi<10^9 思路&#xff1a;贪心&#xff1a;从左到右或者从右到左依次判断每一棵玉米是否可以倒下 &#xff08;以从左到右为例&#xff1a;先往左倒&…

[论文笔记]Root Mean Square Layer Normalization

引言 今天带来论文Root Mean Square Layer Normalization的笔记&#xff0c;论文题目是均方根层归一化。 本篇工作提出了RMSNorm&#xff0c;认为可以省略重新居中步骤。 简介 层归一化对Transformer等模型非常重要&#xff0c;它可以帮助稳定训练并提升模型收敛性&#xf…

生成对抗网络GAN的扩展应用理解

注&#xff1a;本文仅个人学习理解&#xff0c;如需详细内容请看原论文&#xff01; 一、cycleGAN 1.整体架构&#xff1a; 将图片A通过生成器生成图片B&#xff0c;然后通过判别器判别图片B的真假&#xff1b;并且图片B通过另一个生成器生成A‘&#xff0c;让A和A’近似。 2…

Python这十大特征,堪称“圆满”!

当你犹豫是否要开始 Python 学习之前&#xff0c;可以先详尽了解一下这门编程语言。 软件开发者 Guido Van Rossum 于 1991 年创建了 Python&#xff0c;旨在使程序员的工作更加简单。Python 是目前全球比较流行且产业急需的程序设计语言&#xff0c;也是一门跨平台、开源、免…

2024-8.python作用域+函数其他

变量的作用域 讲到了函数就必须介绍变量的作用域相关。 作用域指的是变量的有效范围。变量并不是在哪个位置都可以访问的&#xff0c;访问权限取决于这个变量是在哪里赋值的&#xff0c;也就是在哪个作用域内赋的值。变量在哪个作用域内赋值&#xff0c;则表示该变量的作用域就…

POST 为什么会发送两次请求?

本文目录 同源策略 什么是同源策略 CORS 简单请求 预检请求 附带身份凭证的请求与通配符 完整的请求流程图 总结 前言 最近博主在字节面试中遇到这样一个面试题&#xff0c;这个问题也是前端面试的高频问题&#xff0c;因为在前端开发的日常开发中我们总是会与post请求…

【数据结构】-- 栈和队列

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;python从入门到精通&#xff0c;魔法指针&#xff0c;进阶C&#xff0c;C语言&#xff0c;C语言题集&#xff0c;C语言实现游戏&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文…

Mybatis-plus中的分页操作

Mybatis-plus中的分页操作 1.导入Mybatis-plus依赖2.创建mybatis配置类3.参数 1.导入Mybatis-plus依赖 因为是一个springboot项目&#xff0c;其中的pom.xml文件内容如下&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns&q…

ping命令的使用

一、实验环境 同实验案例分析ARP解析过程环境。 二、需求描述 熟悉 ping 命令的用法并熱悉 ping 命令的各种参数 三、推荐步骤 分别 ping 一个存在的和不存在的IP地址&#xff0c;观察返回的信息分别测试 ping 命令的相关参数。 四、实验步骤 1.ping 一个存在的和不存在…

数据加密、文档加密为什么都选择安企神软件

数据加密、文档加密为什么都选择安企神软件 免费试用安企神 在数据加密和文件加密领域&#xff0c;有众多优秀的软件&#xff0c;他们功能各异、价格不同、效果也大相径庭&#xff0c;经过对比使用、用户口碑和技术网站评判&#xff0c;安企神在各方面都稳坐第一把交易。其原…

ECA-Net:深度卷积神经网络中的高效通道注意力机制【原理讲解及代码!!!】

ECA-Net&#xff1a;深度卷积神经网络中的高效通道注意力机制 在深度学习领域&#xff0c;特别是在深度卷积神经网络&#xff08;DCNN&#xff09;中&#xff0c;注意力机制已经成为提升模型性能的关键技术之一。其中&#xff0c;ECA模块&#xff08;Efficient Channel Attent…

Nginx常用配置,开箱即用

经常遇到Nginx安装和配置的问题。这里笔者将常用配置统统写在下面&#xff0c;方便咱们日常使用。这里本着开箱即用的原则&#xff0c;所以大多数时候不会解释为什么要这样去配置&#xff0c;也不涉及Nginx的安装步骤。下面的所有配置&#xff0c;都可以直接复制后粘贴使用&…

虚幻引擎源码版安装下载,点击GenerateProjectFiles.bat报错 error NU1101NuGet包问题解决参考方案

开发环境配置与源码安装使用 安装VS2022 按照官方文档安装需要的vs配置 虚幻引擎源代码下载 Epic里面下载的引擎与源代码引擎区别&#xff1a;Epic里面下载的引擎是已经编译过的它的源代码访问权限不完整&#xff0c;源代码版本提供比较完整引擎代码&#xff0c;并且可以修…

顺序表(增删减改)+通讯录项目(数据结构)+顺序表专用题型

什么是顺序表 顺序表和数组的区别 顺序表本质就是数组 结构体初阶进阶 系统化的学习-CSDN博客 简单解释一下&#xff0c;就像大家去吃饭&#xff0c;然后左边是苍蝇馆子&#xff0c;右边是修饰过的苍蝇馆子&#xff0c;但是那个好看的苍蝇馆子一看&#xff0c;这不行啊&a…

SAP NWDI(二)服务开启(SLD,CM,CMS)

一、启用System Landscape Directory 二、启用 NWDI using CMS

反射

目录 01、Java反射机制概述1.1、使用反射&#xff0c;实现同上的操作、调用私有属性 02、理解Class类并获取Class实例2.1、Class类的理解2.2、获取Class实例的4种方式2.3、Class实例对应的结构的说明 03、ClassLoader的理解3.1、ClassLoader的理解3.2、使用ClassLoader加载配置…