首页 > 其他分享 >分类树,我从2s优化到0.1s

分类树,我从2s优化到0.1s

时间:2023-08-18 10:08:04浏览次数:43  
标签:缓存 2s 0.1 数据 分类 Redis private 优化

分类树,我从2s优化到0.1s_Redis

但就是这样一个简单的分类树查询功能,我们却优化了5次。

到底是怎么回事呢?

背景

我们的网站使用了SpringBoot推荐的模板引擎:Thymeleaf,进行动态渲染。

它是一个XML/XHTML/HTML5模板引擎,可用于Web与非Web环境中的应用开发。

它提供了一个用于整合SpringMVC的可选模块,在应用开发中,我们可以使用Thymeleaf来完全代替JSP或其他模板引擎,如Velocity\FreeMarker等。

前端开发写好Thymeleaf的模板文件,调用后端接口获取数据,进行动态绑定,就能把想要的内容展示给用户。

由于当时这个是从0-1的新项目,为了开快速开发功能,我们第一版接口,直接从数据库中查询分类数据,组装成分类树,然后返回给前端。

通过这种方式,简化了数据流程,快速把整个页面功能调通了。

第1次优化

我们将该接口部署到dev环境,刚开始没啥问题。

随着开发人员添加的分类越来越多,很快就暴露出性能瓶颈。

我们不得不做优化了。

我们第一个想到的是:加Redis缓存

流程图如下:

分类树,我从2s优化到0.1s_数据_02

于是暂时这样优化了一下:

  1. 用户访问接口获取分类树时,先从Redis中查询数据。
  2. 如果Redis中有数据,则直接数据。
  3. 如果Redis中没有数据,则再从数据库中查询数据,拼接成分类树返回。
  4. 将从数据库中查到的分类树的数据,保存到Redis中,设置过期时间5分钟。
  5. 将分类树返回给用户。

我们在Redis中定义一个了key,value是一个分类树的json格式转换成了字符串,使用简单的key/value形式保存数据。

经过这样优化之后,dev环境的联调和自测顺利完成了。

第2次优化

我们将这个功能部署到st环境了。

刚开始测试同学没有发现什么问题,但随着后面不断地深入测试,隔一段时间就出现一次首页访问很慢的情况。

于是,我们马上进行了第2次优化。

我们决定使用Job定期异步更新分类树到Redis中,在系统上线之前,会先生成一份数据。

当然为了保险起见,防止Redis在哪条突然挂了,之前分类树同步写入Redis的逻辑还是保留。

于是,流程图改成了这样:

分类树,我从2s优化到0.1s_本地缓存_03

增加了一个job每隔5分钟执行一次,从数据库中查询分类数据,封装成分类树,更新到Redis缓存中。

其他的流程保持不变。

此外,Redis的过期时间之前设置的5分钟,现在要改成永久。

通过这次优化之后,st环境就没有再出现过分类树查询的性能问题了。

第3次优化

测试了一段时间之后,整个网站的功能快要上线了。

为了保险起见,我们需要对网站首页做一次压力测试。

果然测出问题了,网站首页最大的qps是100多,最后发现是每次都从Redis获取分类树导致的网站首页的性能瓶颈。

我们需要做第3次优化。

该怎么优化呢?

答:加内存缓存。

如果加了内存缓存,就需要考虑数据一致性问题。

内存缓存是保存在服务器节点上的,不同的服务器节点更新的频率可能有点差异,这样可能会导致数据的不一致性。

但分类本身是更新频率比较低的数据,对于用户来说不太敏感,即使在短时间内,用户看到的分类树有些差异,也不会对用户造成太大的影响。

因此,分类树这种业务场景,是可以使用内存缓存的。

于是,我们使用了Spring推荐的caffine作为内存缓存。

改造后的流程图如下:

分类树,我从2s优化到0.1s_数据_04

  1. 用户访问接口时改成先从本地缓存分类数查询数据。
  2. 如果本地缓存有,则直接返回。
  3. 如果本地缓存没有,则从Redis中查询数据。
  4. 如果Redis中有数据,则将数据更新到本地缓存中,然后返回数据。
  5. 如果Redis中也没有数据(说明Redis挂了),则从数据库中查询数据,更新到Redis中(万一Redis恢复了呢),然后更新到本地缓存中,返回返回数据。

需要注意的是,需要改本地缓存设置一个过期时间,这里设置的5分钟,不然的话,没办法获取新的数据。

这样优化之后,再次做网站首页的压力测试,qps提升到了500多,满足上线要求。

第4次优化

之后,这个功能顺利上线了。

使用了很长一段时间没有出现问题。

两年后的某一天,有用户反馈说,网站首页有点慢。

我们排查了一下原因发现,分类树的数据太多了,一次性返回了上万个分类。

原来在系统上线的这两年多的时间内,运营同学在系统后台增加了很多分类。

我们需要做第4次优化。

这时要如何优化呢?

限制分类树的数量?

答:也不太现实,目前这个业务场景就是有这么多分类,不能让用户选择不到他想要的分类吧?

这时我们想到最快的办法是开启nginxGZip功能。

让数据在传输之前,先压缩一下,然后进行传输,在用户浏览器中,自动解压,将真实的分类树数据展示给用户。

之前调用接口返回的分类树有1MB的大小,优化之后,接口返回的分类树的大小是100Kb,一下子缩小了10倍。

这样简单的优化之后,性能提升了一些。

第5次优化

经过上面优化之后,用户很长一段时间都没有反馈性能问题。

但有一天公司同事在排查Redis中大key的时候,揪出了分类树。之前的分类树使用key/value的结构保存数据的。

我们不得不做第5次优化。

为了优化在Redis中存储数据的大小,我们首先需要对数据进行瘦身。

只保存需要用到的字段。

例如:

@AllArgsConstructor
@Data
public class Category {

    private Long id;
    private String name;
    private Long parentId;
    private Date inDate;
    private Long inUserId;
    private String inUserName;
    private List<Category> children;
}

像这个分类对象中inDate、inUserId和inUserName字段是可以不用保存的。

修改自动名称。

例如:

@AllArgsConstructor
@Data
public class Category {
    /**
     * 分类编号
     */
    @JsonProperty("i")
    private Long id;

    /**
     * 分类层级
     */
    @JsonProperty("l")
    private Integer level;

    /**
     * 分类名称
     */
    @JsonProperty("n")
    private String name;

    /**
     * 父分类编号
     */
    @JsonProperty("p")
    private Long parentId;

    /**
     * 子分类列表
     */
    @JsonProperty("c")
    private List<Category> children;
}

由于在一万多条数据中,每条数据的字段名称是固定的,他们的重复率太高了。

由此,可以在json序列化时,改成一个简短的名称,以便于返回更少的数据大小。

这还不够,需要对存储的数据做压缩。

之前在Redis中保存的key/value,其中的value是json格式的字符串。

其实RedisTemplate支持,value保存byte数组

先将json字符串数据用GZip工具类压缩成byte数组,然后保存到Redis中。

再获取数据时,将byte数组转换成json字符串,然后再转换成分类树。

这样优化之后,保存到Redis中的分类树的数据大小,一下子减少了10倍,Redis的大key问题被解决了。

原文:https://mp.weixin.qq.com/s/rDLkysI4fh6mG4XewVoNIQ


标签:缓存,2s,0.1,数据,分类,Redis,private,优化
From: https://blog.51cto.com/song/7131751

相关文章

  • React请求机制优化思路
    说起数据加载的机制,有一个绕不开的话题就是前端性能,很多电商门户的首页其实都会做一些垂直的定制优化,比如让请求在页面最早加载,或者在前一个页面就进行预加载等等。随着react18的发布,请求机制这一块也是被不断谈起,并且在后续其实也给出了明确的方向。假如我们页面中有三个组件C1......
  • 定时同步数据优化
    定时同步数据优化前言定时任务在系统中并不少见,主要目的是用于需要定时处理数据或者执行某个操作的情况下,如定时关闭订单,或者定时备份。而常见的定时任务分为2种,第一种:固定时间执行,如:每分钟执行一次,每天执行一次。第二种:延时多久执行,就是当发生一件事情后,根据这件时间发生的时间......
  • sql优化
    sql优化由4个部分,第一个是硬件和操作系统成面优化,第二个是架构设计层优化,第三个是程序配置优化,第四个是sql执行优化。第一个从硬件层面上来说影响mysql性能因素主要是cpu可用内存大小磁盘读写速度网络带宽从操作系统层面上来说应用文件句柄数操作系统的网络配置都会......
  • dp 凸优化学习笔记
    好久没系统地写一个算法相关内容的学习笔记了,主要是我学习dp凸优化部分有意义,有象征性的例题。目前网上很多题解都有点讲的不明不白的感觉,很多甚至都连基本知识都没说清楚就开始SlopeTrick了,这困扰了我许久。我认为通过这篇文章可以比较清晰地了解dp凸优化的入门知识和......
  • 解密数据库连接池的奥秘与优化策略
    在后端开发中,数据库是应用程序的核心数据存储和管理部分。为了高效地处理数据库连接,提升应用性能,数据库连接池成为了不可或缺的技术。本文将深入探讨数据库连接池的工作原理,并介绍一些优化策略,帮助你更好地理解和使用数据库连接池。数据库连接池的背景与作用数据库连接是有代价的资......
  • .net core发布到IIS上出现 HTTP 错误 500.19
    ​1.检查.netcore环境运行环境是否安装完成,类似如下环境​编辑 2.IIS是否安装全本次原因就是IIS未安装全导致的按照网上说的手动重启iis(iisreset)也不行 ​......
  • Matlab灰狼算法(GWO)优化双向长短期记忆神经网络的数据分类预测,GWO-BiLSTM分类预测,多
    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 企业内容建站系统 ModStartCMS v7.0.0 多语言开发优化,多个常用组件升级
    ModStart是一个基于Laravel模块化极速开发框架。模块市场拥有丰富的功能应用,支持后台一键快速安装,让开发者能快的实现业务功能开发。系统完全开源,基于Apache2.0开源协议,免费且不限制商业使用。功能特性丰富的模块市场,后台一键快速安装会员模块通用且完整,支持完整的API调用大......
  • 如何用随机方法求解组合优化问题(五)
    模拟退火算法这是一篇笔记,是对于B站up主马少平的视频(第四篇如何用随机方法求解组合优化问题(五))的学习与记录。前置知识回顾【回顾】:局部最优问题在局部搜索问题中,可能会陷入局部最优解(如上图中的B、C),解决思路是:以概率接受差解。【回顾】:退火过程中从状态\(i\)转换为状......
  • OCPP2.0.1充电桩测试工具
    当前市场对OCPP2.0.1的需求越来越多。但我找遍互联网也没有找到一个可用的测试工具,于是决定自己开发一个。测试工具是BS架构的,直接上界面服务器触发指令有预设参数,且会以json的方式展示,可以直接编辑,并展示了schema,方便查询参数更多功能持续开发中...博主本人有丰富......