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

news2024/11/29 7:43:43

前言

分类树查询功能,在各个业务系统中可以说随处可见,特别是在电商系统中。

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

到底是怎么回事呢?

背景

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

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

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

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

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

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

第1次优化

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

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

我们不得不做优化了。

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

流程图如下:

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

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

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

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

第2次优化

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

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

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

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

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

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

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

其他的流程保持不变。

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

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

第3次优化

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

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

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

我们需要做第3次优化。

该怎么优化呢?

答:加内存缓存。

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

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

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

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

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

改造后的流程图如下:

  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问题被解决了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

保经济、促创新,汽车行业综合采购(系统)数智化解决方案

汽车行业是中国国民经济的支柱产业&#xff0c;公安部数据显示&#xff0c;我国平均每百户家庭拥有汽车达到60辆。广阔的市场为行业带来大量需求&#xff0c;以及激烈的市场竞争&#xff1b;同时我国汽车产业正处在从传统工业时代向数字时代迈进的关键时期&#xff0c;急需创新…

【Rust日报】2023-06-07 使用 C++ 编写通用库并在 Rust 中使用它 (WASI)

使用 C 编写通用库并在 Rust 中使用它 (WASI) WebAssembly 简介 WebAssembly 是一种二进制指令格式&#xff0c;旨在成为一种低级虚拟机&#xff0c;可以在 Web 浏览器中以接近本机的速度运行代码。它不特定于网络&#xff0c;也可以在其他平台上运行。WebAssembly 代码可以从各…

【嵌入式环境下linux内核及驱动学习笔记-(15-1)例程】

目录 1、在APP直接调用标准文件IO操作I2C(针对学习笔记-15的15.3节)1.1 mail.c1.2 mpu6050.h1.3 mpu6050.c1.4 Makefile 2、以外称id的方式进行匹配的i2c驱动2.1 mpu6050.h2.2 mpu6050_i2c_client.c2.3 mpu6050_i2c_driver.c2.4 read_mpu.c 测试的应用层APP2.5 Makefile 3、以设…

Python3数据分析与挖掘建模(10)复合分析-交叉分析与实现示例

1. 复合分析 1.1 概述 复合分析&#xff08;Factorial Analysis&#xff09;是一种统计分析方法&#xff0c;用于研究多个因素对观测结果的影响&#xff0c;并探究各个因素之间的相互作用效应。 在复合分析中&#xff0c;研究者会选择多个因素&#xff08;也称为处理变量或独…

武汉涉密系统集成资质保密制度涵盖哪些内容

涉密信息系统集成资质申请其中最核心的就是保密制度管理&#xff0c;这里面涉及到保密管理的方方面面&#xff0c;现场审查也是围绕保密制度建设内容来打分的。申报单位应当建立规范、操作性强的保密制度&#xff0c;并根据实际情况及时修订完善。保密制度的具体要求应当体现在…

highlight clock tree

当分析clock tree需要在图形界面highlight clock tree时&#xff0c;最朴实无华的方法就是贴报告&#xff0c;除此之外这里也分享一下用命令的方法。 1.Imported Path Pins 1&#xff09;Highlight > Color By > Imported Path Pins 2&#xff09;report_timing icc2…

智慧园区物业可视化大屏

随着万物互联、数字信息时代的到来&#xff0c;也给物业园区管理行业带来变革性影响。 园区作为城市的基本组成单元&#xff0c;是人口和产业的重要聚集区&#xff0c;现已逐渐成为中国经济转型升级和创新发展的主力。 智慧园区物业可视化整合园区现有信息系统的数据资源&#…

OpenStack部署(四)

OpenStack部署 8. Dashboard8.1 安装并配置8.2 重启web服务器以及会话存储服务8.3 浏览器访问配置 9. Designate9.1 创建Designate数据库并授权9.2 获得admin凭证9.3 创建designate用户并设置密码9.4 添加admin角色到designate用户9.5 创建designate服务实体9.6 创建designate服…

【计算机组成与体系结构Ⅰ】章节测试(1-3)

下列是计算机中几种常见的机器数编码&#xff1a; ①原码 ②反码 ③补码 零的表示唯一的是&#xff08; &#xff09; A&#xff0e;仅③ B&#xff0e;② C&#xff0e;①、② D&#xff0e;①、③ 下列采用偶校验的8位奇偶校验编码中正确的是&#xff08; &#xff09;…

【Spring】——Spring简单 读和取

前言 ❤️❤️❤️Spring专栏更新中&#xff0c;各位大佬觉得写得不错&#xff0c;支持一下&#xff0c;感谢了&#xff01;❤️❤️❤️ Spring_冷兮雪的博客-CSDN博客 上期我们讲解了Spring的创建与使用&#xff0c;发现 将Bean 注册到容器 这一步中&#xff0c;如果Bean对象…

Matter协议高速崛起,你真的了解它吗?

今天我们要聊的话题&#xff0c;和智能家居有关。 说到智能家居&#xff0c;大家应该都不会陌生。早在本世纪初&#xff0c;物联网概念刚刚诞生的时候&#xff0c;最主要的应用领域&#xff0c;就是智能家居。 这些年来&#xff0c;随着数字技术的不断发展&#xff0c;越来越多…

MINIX 已死,Linux 又将如何呢?

导读MINIX 操作系统大约的确已经死了。Minix 原来是荷兰阿姆斯特丹的 Vrije 大学计算机科学系的 Andrew S. Tanenbaum 教授所开发的一个类 Unix 操作系统&#xff0c;全部代码共约 12,000 行&#xff0c;起初是为给学生讲解操作系统的运作细节而开发。 Linus Torvalds 也曾表示…

报表生成器FastReport .Net用户指南:“Rich Text“对象

FastReport .Net是一款全功能的Windows Forms、ASP.NET和MVC报表分析解决方案&#xff0c;使用FastReport .NET可以创建独立于应用程序的.NET报表&#xff0c;同时FastReport .Net支持中文、英语等14种语言&#xff0c;可以让你的产品保证真正的国际性。 FastReport.NET官方版…

1999-2020年31省省农村人口就业和文化程度相关数据

1999-2020年31省省农村人口就业和文化程度相关数据 1、时间&#xff1a; 2、范围&#xff1a;包括全国31省 3、来源&#xff1a;整理自各省NJ、统计NJ、农村NJ 4、指标包括&#xff1a; 乡村人口和乡村就业人员&#xff1a;乡村人口&#xff08;万人&#xff09;、乡村人口&…

opencv实践项目-停车位检测

目录 1. 步骤1.1 selector选择器1.2 detector探测器 2. 代码3. 效果图 1. 步骤 1.1 selector选择器 我们可以选择摄网络摄像头提供的第一帧&#xff0c;在该图像上选择停车位。为此&#xff0c;保存并使用该图像选择停车位。使用selectROIs函数标记停车位。ROI被定义为感兴趣…

分布式限流算法及实现介绍

分布式系统架构下面对突增的高并发访问请求&#xff0c;如何实现限流以保护系统的可用性是需要关注的一个问题。分布式限流实现机制上有很多中&#xff0c;包括基于网关实现、基于中间件如Redis实现等&#xff0c;本文简要介绍限流的常用算法以及实现方案。 1、分布式限流概述 …

Windows操作系统渗透测试

Windows操作系统渗透测试 任务环境说明&#xff1a;服务器场景名&#xff1a;Server02服务器场景操作系统&#xff1a;未知&#xff08;关闭链接&#xff09; 1.通过本地PC中渗透测试平台Kali对服务器场景进行系统服务及版本扫描渗透测试&#xff0c;并将该操作显示结果中808…

可视化报表系统推荐

在当今信息时代&#xff0c;数据的处理和分析已经成为了企业管理中不可或缺的一部分。而报表则是这个过程中最常见的工具之一。手工写报表虽然简单易懂&#xff0c;但是随着数据量的增加&#xff0c;这种方式逐渐暴露出许多痛点。比如说&#xff1a; 1.时间耗费长&#xff1a;…

Linux之进程间通信——管道

文章目录 前言一、进程间通信1.概念2.目的3.进程间通信分类 二、管道1.管道介绍2.管道分类1.匿名管道pipi创建管道文件&#xff0c;打开读写端fork子进程关闭父进程的读取端&#xff0c;关闭子进程的写入端读写特征管道特征 2.命名管道mkfifo创建管道文件删除管道文件通信 三、…

【openframework】实时路径规划(RTRRTstar算法)

程序框架 视频演示 实时RRT-star算法介绍 实时RRT-star算法是一种基于采样的运动规划算法&#xff0c;它可以在有限的时间内找到一条渐进最优的路径。实时RRT-star算法是在RRT-star算法的基础上进行了改进&#xff0c;主要有两个方面&#xff1a; - 实时更新起始点。实时RRT-st…