前言

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

但就是这样一个简单的分类树查询功能,我们却优化了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问题被解决了。

最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描下发二维码关注一下,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的10万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。

从2s优化到0.1s的更多相关文章

  1. MySQL——查询优化|47s到0.1s|我做了什么

    前言 这个代码是之前的同事写的,现在我接管了,但是今天早上我打开这个模块的时候发现数据加载异常的缓慢,等了将近一分钟左右数据才显示到页面. 这特么的绝对不正常啊,数据量压根没那么多呀,这特喵的什么情况 ...

  2. 操作系统性能分析与优化V1.0

    操作系统性能分析与优化V1.0 : http://www.docin.com/p-759561760.html

  3. 记一次重大生产事故,在那 0.1s 我想辞职不干了!

    一.发生了什么? 1.那是一个阳光明媚的下午,老婆和她的闺蜜正在美丽的湖边公园闲逛(我是拎包拍照的). 2.突然接到甲方运营小妹的微信:有个顾客线上付款了,但是没有到账,后台卡在微信支付成功(正常状态 ...

  4. 升级openssl 到 1.0.1s 最新版

    1.下载 wget http://www.openssl.org/source/openssl-1.0.1s.tar.gz 2.解压 tar -zxf openssl-1.0.1s.tar.gz cd ...

  5. 前端优化 - 打开速度1s

    先看一下网页的加载流程: 1.解析html结构2.加载外部脚本和样式表文件3.解析并执行脚本(脚本会阻塞页面的加载)4.DOM树构建完成 (DOMContentLoaded)5.加载图片等外部文件6. ...

  6. 将 子集和问题 运行时间从 200.8s 优化到 0.4s

    在过去24小时里,一直被这题折腾着... 题目: A Math gameTime Limit: 2000/1000MS (Java/Others) Memory Limit: 256000/12800 ...

  7. 小米2S TWRP 3.0.2-0 最新中文版本Recovery

    注意:此版本为合并分区后的twrp  小米2S 合并分区教程:http://forum.xda-developers.com/mi-2/orig-development/flashtools-mifl ...

  8. poj 1258 Agri-Net 最小生成树 prim算法+heap不完全优化 难度:0

    Agri-Net Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 41230   Accepted: 16810 Descri ...

  9. 快速切题 poj 2485 Highways prim算法+堆 不完全优化 难度:0

    Highways Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 23033   Accepted: 10612 Descri ...

  10. 小米2S TWRP 3.0.2-0 最新版Recovery

    主界面 使用了我最新修改的内核 下载地址: 链接: http://pan.baidu.com/s/1i5xwddb 密码: 7dyb 验证信息: md5sum: dca410f33020eb87986 ...

随机推荐

  1. 零基础学习人工智能—Python—Pytorch学习(九)

    前言 本文主要介绍卷积神经网络的使用的下半部分. 另外,上篇文章增加了一点代码注释,主要是解释(w-f+2p)/s+1这个公式的使用. 所以,要是这篇文章的代码看不太懂,可以翻一下上篇文章. 代码实现 ...

  2. .NET 开源报表神器 Seal-Report

    前言 Seal-Report 是一款.NET 开源报表工具,拥有 1.4K Star.它提供了一个完整的框架,使用 C# 编写,最新的版本采用的是 .NET 8.0 . 它能够高效地从各种数据库或 N ...

  3. psutil 检测exe是否已经运行

    安装依赖 pip install psutil 代码 import psutil def check_if_process_running(process_name): ''' Check if th ...

  4. AWS Cloud Practioner 官方课程笔记 - Part 3

    AWS Security 方案和功能 Amazon Inspector AWS Shield Price and Support Free Tier: Always Free, 12-month fr ...

  5. Angular 18+ 高级教程 – Reactive Forms

    前言 上一篇的 Ajax 和这一篇的表单 (Form) 都是前端最最最常见的需求. 为此,Angular 分别提供了两个小型库来帮助开发者实现这些需求: Ajax – HttpClient Form ...

  6. CSS – Monospaced font & ch unit 等宽字体与 ch 单位

    前言 在做 Statistics Counter 时, 发现总是会跳, 研究后才发现原来是等宽搞的鬼, 这篇就来说说等宽字体. 参考 等宽字体在web布局中应用以及CSS3 ch单位嘿嘿 不等宽字体 ...

  7. flink 大批量任务提交 yarn 失败问题

    问题现象 用户迁移到新集群后,反馈他们开发平台大量 flink 任务提交失败了,当时集群的 yarn 资源是足够的 排查过程 用户是在他们的开发平台上提交的,查看他们失败的任务,发现是他们提交端主动 ...

  8. [OI] Kruskal 重构树

    算法介绍 Kruskal 重构树用于快速判断节点的连通性. 考虑到,假如两个节点是联通的,则他们之间总会有一条边被选入最小生成树内,因此他们在最小生成树内也是联通的. 也就是说,我们可以通过求最小生成 ...

  9. SpringBoot 集成 LDAP

    LDAP协议具体是什么可以自行查看,简单来说就是单点登录的一种实现方式 LDAP只是一种协议,实现的有 openLDAP ,Microsoft active directory 等 openLDAP部 ...

  10. 单Master节点的k8s集群部署-完整版

    K8S 安装步骤 一.准备工作 1.准备三台主机(一台Master节点,两台Node节点)如下: 角色 IP 内存 核心 磁盘 Master 192.168.116.131 4G 4个 55G Nod ...