谷粒商城 - 树形菜单递归流查询、三级分类数据查询性能优化、Jmter 性能压测

目录

树形分类菜单(递归查询,强扩展)

1)需求

2)数据库表设计

3)实现

4)关于 asSequence 优化

性能压测

1)Jmeter 安装使用说明

2)中间件对性能的影响

三级分类数据查询性能优化

需求分析

1)未优化

2)第一次优化(数据库一次查询)

3)第二次优化(SpringCache 整合 Redis)

4)3 种不同实现性能测试


树形分类菜单(递归查询,强扩展)


1)需求

展示如下属性分类菜单

实际上是一个三级分层目录,并且考虑到将来可能会拓展成为 四级、五级... 目录.

2)数据设计

a)表设计如下

CREATE TABLE `pms_category` (`cat_id` bigint NOT NULL AUTO_INCREMENT COMMENT '分类id',`name` char(50) DEFAULT NULL COMMENT '分类名称',`parent_cid` bigint DEFAULT NULL COMMENT '父分类id',`cat_level` int DEFAULT NULL COMMENT '层级',`show_status` tinyint DEFAULT NULL COMMENT '是否显示[0-不显示,1显示]',`sort` int DEFAULT NULL COMMENT '排序',`icon` char(255) DEFAULT NULL COMMENT '图标地址',`product_unit` char(50) DEFAULT NULL COMMENT '计量单位',`product_count` int DEFAULT NULL COMMENT '商品数量',PRIMARY KEY (`cat_id`),KEY `parent_cid` (`parent_cid`)
) ENGINE=InnoDB AUTO_INCREMENT=1433 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品三级分类';

b)vo 设计如下

data class CategoryTreeVo (val catId: Long,val name: String,val parentCid: Long, //父分类 idval catLevel: Int, //层级val showStatus: Int, //是否显示 (0不显示 1显示)val sort: Int,val icon: String?, //图标地址,val productUnit: String?, //计量单位val productCount: Int, //商品数量var children: List<CategoryTreeVo> //孩子节点
)

3)实现

这里我们可以先将 pms_category 表中所有数据取出来,然后在内存中通过流的方式来操作数据.

具体的流操作,如下:

  1. filter:过滤出一级菜单分类.
  2. map:通过递归的方式来查询子菜单分类,包装到 vo 中
    1. filter:先从所有的数据中过滤出当前菜单的子菜单
    2. map:递归的方式继续查询子菜单,包装到 vo 中.
      1. ......
  3. sortedBy:按照 sort 字段来进行升序排序.
@Service
class CategoryServiceImpl(private val categoryRepo: CategoryRepo,
): CategoryService {override fun listWithTree(): List<CategoryTreeVo> {//1.获取所有分类val vos = categoryRepo.queryAll().map(::map)//2.分级val result = vos.asSequence() //数据量较大,使用 asSequence 有优化.filter { category -> //1) 找到所有一级分类category.catLevel == 1}.map { category -> //2) 递归的去找 children 分类category.children = getCategoryChildrenDfs(category, vos)return@map category}.sortedBy { // 降序 sortedByDescending { it.sort }it.sort}.toList()return result}/*** 递归的去找 children 分类*/private fun getCategoryChildrenDfs(root: CategoryTreeVo,all: List<CategoryTreeVo>): List<CategoryTreeVo> {//递归终止条件: 当 filter 过滤出来的数据为空,就直接返回 空list,不会走下一个 map 逻辑了val result = all.filter { category -> //1.从所有分类中找出父节点为当前节点(找到当前节点的所有孩子节点)category.parentCid == root.catId}.map { category -> //2.递归的去找孩子category.children = getCategoryChildrenDfs(category, all)return@map category}.sortedBy { category ->category.sort}.toList()return result}fun map(obj: Category) = with(obj) {CategoryTreeVo (catId = catId,name = name,parentCid = parentCid,catLevel = catLevel,showStatus = showStatus,sort = sort,icon = icon,productUnit = productUnit,productCount = productCount,children = emptyList(),)}}

4)关于 asSequence 优化

这里我也做了一个压测(1min/100用户并发)

没有使用 asSequence 如下:

使用 asSequence 如下:

Ps:asSequence 在处理大数据量时速度更快的原因主要是因为它采用了惰性求值策略,避免了不必要的多次迭代和中间集合的创建(原本的集合操作,每进行例如 filter 就会创建中间集合),从而减少了内存和处理时间的消耗。这种优化在处理大数据集时尤其显著

性能压测


1)Jmeter 安装使用说明

a)安装

Apache JMeter - Download Apache JMeter

解压后点击 \apache-jmeter-5.6.3\bin 目录下的 jmeter.bat 即可.

b)参数说明

吞吐量:每秒处理的请求个数 

一般自己测的时候,主要观察这两个指标即可.

关于吞吐量,这里可以给出一个业界的标准~

  • 电商网站

    • 小型:几十到几百 RPS (10-500)
    • 中型:几百到几千 RPS (500-5,000)
    • 大型:几千到数万 RPS (5,000-50,000)
  • 社交媒体平台

    • 中型:几千到几万 RPS (5,000-50,000)
    • 大型:数万到数十万 RPS (50,000-500,000)
  • 流媒体服务

    • 小型:几百到几千 RPS (500-5,000)
    • 大型:数千到数万 RPS (5,000-50,000)
  • 在线游戏服务器

    • 小型:几十到几百 RPS (10-500)
    • 大型:几千到几万 RPS (5,000-50,000)

2)中间件对性能的影响

这里我们对当前系统进行一个性能测试,先来看看中间件(例如 nginx、网关...)对系统的影响.

测试内容线程数吞吐量/s
网关(直接将请求打到网关端口即可,404 也算正常返回)5025262
简单服务(直接将请求打到对应的微服务即可)5039234
网关 + 简单服务(服务就简单的返回一个 hello 字符串即可)5012072
  • 分析:引入中间件会带来更大的网络开销. 
    • 起初,只需要客户端和服务之间进行通讯.
    • 引入网关后,需要 客户端先和网关通讯,再有网关和服务通讯,最后在原路返回响应.
  • 结论:中间件越多,性能损耗越大.
  • 优化:考虑跟高效的网络协议、买更好的网卡,增加网络带宽...

三级分类数据查询性能优化


需求分析

a)需要给前端返回的结果是一个 json 结构数据,格式如下:

最外层是一个 Map<String, List<Any>> 的结构. key 就是一级分类id. value 是一个对象数组

这个对象就是 二级分类 的数据

二级分类中又包含该分类下的三级分类列表

对应 data class 如下:

//二级分类数据
data class Catalog2Vo (val catalog1Id: String, //一级分类 id (父分类 id)val catalog3List: List<Catalog3Vo>, //三级子分类val id: String,val name: String,
)//三级分类数据
data class Catalog3Vo (val catalog2Id: String, //二级分类 id (父分类 id)val id: String,val name: String,
)

最后给前端返回 Map<String, List<Catalog2Vo>> 数据.  key 是一级分类 id

1)未优化

a)实现方式:

最直接的方法就是先从数据库中查到所有一级分类数据,然后再拿着每一个一级分类 id 去查对应的二级分类数据,最后拿着每个二级分类的 id 去查对应的三级分类数据.

    override fun getCatalogJson(): Map<String, List<Catalog2Vo>> {//1.查询所有一级分类val level1List = categoryRepo.queryLevel1CategoryAll()//2.封装 二级 -> 三级 分类val result = level1List.associate { l1 -> l1.catId.toString() to run {//1) 查到这个一级分类中的所有二级分类val level2Vos = categoryRepo.queryCategoryByParentId(l1.catId).map { l2 ->//2) 查到这个二级分类中所有三级分类val leve3Vos = categoryRepo.queryCategoryByParentId(l2.catId).map { l3 -> Catalog3Vo(l2.catId.toString(), l3.catId.toString(), l3.name) }//3) 将 三级分类List 整合到 二级分类Listreturn@map Catalog2Vo(l1.catId.toString(), leve3Vos, l2.catId.toString(), l2.name)}return@run level2Vos} }return result}

b)问题:

查询效率非常低,循环套循环频繁的和数据建立和断开连接,带来很大一部分网络开销.

2)第一次优化(数据库一次查询)

a)实现方式:

为了避免大量数据库连接,可以换一个思路~

一开始就从数据库中拿到 分类表 中的所有数据,然后在内存中操作,过滤出每一个一级分类下的所有二级分类数据.......

    override fun getCatalogJson(): Map<String, List<Catalog2Vo>> {//1.查询所有分类数据val all = categoryRepo.queryAll()//2.查询所有一级分类数据val level1List = getCategoryByParentId(all, 0L)//2.封装 二级 -> 三级 分类val result = level1List.associate { l1 -> l1.catId.toString() to run {//1) 查到这个一级分类中的所有二级分类val level2Vos = getCategoryByParentId(all, l1.catId).map { l2 ->//2) 查到这个二级分类中所有三级分类val leve3Vos = getCategoryByParentId(all, l2.catId).map { l3 -> Catalog3Vo(l2.catId.toString(), l3.catId.toString(), l3.name) }//3) 将 三级分类List 整合到 二级分类Listreturn@map Catalog2Vo(l1.catId.toString(), leve3Vos, l2.catId.toString(), l2.name)}return@run level2Vos} }return result}//从所有数据中过滤出指定 parentId 的数据private fun getCategoryByParentId(all: List<Category>, parentId: Long): List<Category> {return all.filter { it.parentCid == parentId }}

3)第二次优化(SpringCache 整合 Redis)

对于分类数据这种每次在内存中计算很耗时,并且更新频率低的数据就非常适合保存到 Redis 缓存中.

a)实现方式:

直接使用 SpringCache 整合 Redis 对数据进行缓存

    @Cacheable(value = ["category"], key = "#root.methodName")override fun getCatalogJson(): Map<String, List<Catalog2Vo>> {println("查询了数据库...")return getCatalogJsonFromDb()}//三级分类查询(数据库一次查询)fun getCatalogJsonFromDb(): Map<String, List<Catalog2Vo>> {//1.查询所有分类数据val all = categoryRepo.queryAll()//2.查询所有一级分类数据val level1List = getCategoryByParentId(all, 0L)//2.封装 二级 -> 三级 分类val result = level1List.associate { l1 -> l1.catId.toString() to run {//1) 查到这个一级分类中的所有二级分类val level2Vos = getCategoryByParentId(all, l1.catId).map { l2 ->//2) 查到这个二级分类中所有三级分类val leve3Vos = getCategoryByParentId(all, l2.catId).map { l3 -> Catalog3Vo(l2.catId.toString(), l3.catId.toString(), l3.name) }//3) 将 三级分类List 整合到 二级分类Listreturn@map Catalog2Vo(l1.catId.toString(), leve3Vos, l2.catId.toString(), l2.name)}return@run level2Vos} }return result}//从所有数据中过滤出指定 parentId 的数据private fun getCategoryByParentId(all: List<Category>, parentId: Long): List<Category> {return all.filter { it.parentCid == parentId }}

4)3 种不同实现性能测试

50 个线程并发

a)未优化

b)第一次优化 (数据库一次查询)

c)第二次优化(SpringCache 整合 Redis)

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

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

相关文章

【Kubernetes】Pod 资源调度之亲和性调度

Pod 资源调度之亲和性调度 1.Node 亲和性调度1.1 Node 硬亲和性1.2 Node 软亲和性 2.Pod 亲和性调度2.1 Pod 硬亲和2.2 Pod 软亲和2.3 Pod 反亲和 Kubernetes 的 默认调度器 以 预选、优选、选定机制 完成将每个新的 Pod 资源绑定至为其选出的目标节点上&#xff0c;不过&#…

吴恩达深度学习笔记:机器学习策略(2)(ML Strategy (2)) 2.7-2.8

目录 第三门课 结构化机器学习项目&#xff08;Structuring Machine Learning Projects&#xff09;第二周&#xff1a;机器学习策略&#xff08;2&#xff09;(ML Strategy (2))2.7 迁移学习&#xff08;Transfer learning&#xff09; 第三门课 结构化机器学习项目&#xff0…

学习笔记——动态路由——IS-IS中间系统到中间系统(区域划分)

三、IS-IS区域划分 根据IS-IS路由器邻居关系&#xff0c;可以将IS-IS划分为两个区域——骨干区域和非骨干区域。&#xff08;注意&#xff0c;这里的区域不是上文中提到的Area ID&#xff09;由L2的IS-IS邻居构成的区域为骨干区域&#xff0c;由L1的IS-IS邻居构成的区域为非骨…

智能运维场景探索 | 运营分析

【本场景来源于 擎创科技《一体化数智运维AIOps解决方案》白皮书&#xff0c;经过重新编写】 该场景主要围绕生产运行、运营决策两个维度进行展开&#xff0c;通过对配置、性能、业务等运行数据的加工计算&#xff0c;形成可量化运营效果、可衡量发展方向的运营数据。整体以低…

基于Qt实现的PDF阅读、编辑工具

记录一下实现pdf工具功能 语言&#xff1a;c、qt IDE&#xff1a;vs2017 环境&#xff1a;win10 一、功能演示&#xff1a; 二、功能介绍&#xff1a; 1.基于saribbon主体界面框架&#xff0c;该框架主要是为了实现类似word导航项 2.加载PDF放大缩小以及预览功能 3.pdf页面跳转…

Python基础小知识问答系列-高效遍历多个不同类型元素的迭代器

1. 问题&#xff1a; 当需要对多个迭代器进行相同遍历操作时&#xff0c;如何避免因为迭代器之间的类型或者迭代器元素 数量过大引发的问题&#xff1f; 2. 解决方法&#xff1a; 使用itertools模块中的chain函数。 示例&#xff1a; from itertools import chainlist_a [2,…

matlab 抛物线图像绘制

抛物线图像绘制 x^2y4绘制结果 x^2y4 clc,clear,close all; length10; % 创建一个范围内的 x 和 y 值 x linspace(-length, length, 1000); y linspace(-length, length, 1000);% 创建一个网格来表示 x 和 y 值的组合 [X, Y] meshgrid(x, y);% 计算方程的左边和右边的值 LH…

QWidget成员函数功能和使用详细说明(四)(文字+用例+代码+效果图)

文章目录 1.测试工程配置2.成员函数2.1 void setParent(QWidget *parent)2.2 void setMouseTracking(bool enable)2.3 bool hasMouseTracking() const2.4 void setPalette(const QPalette &)2.5 const QPalette &palette() const2.6 int QWidget::grabShortcut(const Q…

实战干货,企业在数字化转型中如何通过最佳实践落地BI报表?

引言&#xff1a;上一篇文章我们提到&#xff1a;通过9大步骤&#xff0c;帮助企业在数字化转型中搭建数据分析的报表体系&#xff01;在实际中的落地过程&#xff0c;通过实施服务的哪些最佳实践可以确保落地效果&#xff0c;达到项目预期目标&#xff0c;给客户带来实质价值&…

香蕉云编+uniapp打包ios的开发包和生产包

登录香蕉云编&#xff0c;找到 云编-ios证书生成&#xff0c;新建CSR文件&#xff0c;并下载csr文件。 登录苹果开发者中心&#xff0c;进入证书页面。 1.创建一个证书&#xff0c;选择ios Distribution类型即可&#xff0c;这是个通用的证书&#xff0c;既能用来打正式包又能…

“郭有才”商标主要类别都已被注册!

前阵山东网红“郭有才”火遍大江北&#xff0c;当然少不了许多想去申请注册“郭有才”商标名称的&#xff0c;普推商标知产老杨检索&#xff0c;发现“郭有才”商标申请了43个类别&#xff0c;基本上类别都被申请注册&#xff0c;已注册的商标大多是在“郭有才”火之前申请注册…

MyBatis入门程序详解

目录 一、MyBatis概述 二、编写MyBatis入门程序 三、配置SQL提示 四、传统jdbc的劣势 一、MyBatis概述 MyBatis是一个基于Java的持久层框架&#xff0c;它内部封装了JDBC操作&#xff0c;使得开发人员可以更专注于SQL语句本身而非繁琐的JDBC操作细节。在MyBatis中&#xff0…

最新全平台无人直播硬改XCMS系统,支持任何平台

软件功能: 改虚拟摄像头为真实摄像头&#xff0c;改真实麦克风&#xff0c;图层去重、镜头晃动、增加噪点去重、随机播放辅音&#xff0c;两条音轨帮助音频去重、随机音效、随机播放速度&#xff0c;直播源实时转播等等.防违规&#xff0c;防非实时 设备需求: 电脑&#xf…

纯javascript实现图片批量压缩打包zip下载后端ThinkPHP多国语言切换国际站

最近在做一个多国语言的工具站&#xff0c;需要实现多国语言切换&#xff0c;说到多国语言站&#xff0c;肯定是有2种方式&#xff0c;第一是子域名&#xff0c;第二就是子目录。根据自己的需要来确定。 后台配置如下&#xff1a; 前台显示&#xff1a; 前端纯javascript实现…

【深度学习】图形模型基础(5):线性回归模型第三部分:线性回归模型拟合

1.引言 本博文专辑的焦点主要集中在回归模型的实用案例和工具上&#xff0c;从简单的单变量线性回归入手&#xff0c;逐步过渡到包含多个预测变量、非线性模型&#xff0c;以及在预测和因果推断中的应用。本文我们将介绍回归模型推断的一些数学结构&#xff0c;并提供一些代数…

初识STM32:芯片基本信息

STM32简介 STM32是ST公司基于ARM公司的Cortex-M内核开发的32位微控制器。 ARM公司是全球领先的半导体知识产权&#xff08;IP&#xff09;提供商&#xff0c;全世界超过95%的智能手机和平板电脑都采用ARM架构。 ST公司于1987年由意大利的SGS微电子与法国的Thomson半导体合并…

【Linux】:进程创建与终止

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关Linux程序地址空间的相关知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从…

嵌入式开发者应该有的心态自信

各位开发者大家好,今天主要给大家分享一下,嵌入式开发者应该有的心态,缓解工作和生活中的压力,也是非常重要的。 真的。 世界就是个巨大的草台班子,自信点,别人没有你想象的那么厉害。 普通人最大的问题,就是过分高估别人,并且过分贬低自己。 一、对那些看似厉害的人…

DAY20-力扣刷题

1.填充每个节点的下一个右侧节点指针 116. 填充每个节点的下一个右侧节点指针 - 力扣&#xff08;LeetCode&#xff09; 方法一&#xff1a;层次遍历 class Solution {public Node connect(Node root) {if (root null) {return root;}// 初始化队列同时将第一层节点加入队列…

Hadoop权威指南-读书笔记-02-关于MapReduce

Hadoop权威指南-读书笔记 记录一下读这本书的时候觉得有意思或者重要的点~ 还是老样子~挑重点记录哈&#x1f601;有兴趣的小伙伴可以去看看原著&#x1f60a; 第二章 关于MapReduce MapReduce是一种可用于数据处理的编程模型。 MapReduce程序本质上是并行运行的&#xff0c…