首页/新闻资讯/正文详情

从 8.38秒 到 300毫秒:记一次供给列表接口的极致性能调优实战

发布时间:2026/9/27 22:33:59 来源:云帆数科 栏目:资讯中心
从 8.38秒 到 300毫秒:记一次供给列表接口的极致性能调优实战
从 8.38秒 到 300毫秒记一次供给列表接口的极致性能调优实战前言在日常的搬砖生涯中我们经常会碰到一些“平时用起来没感觉一旦数据量上去或并发一高就直接卡死”的慢接口。最近我手头就遇到了一个硬骨头 ——/system/supply/list供给列表接口。在测试环境中仅仅分页查询 10 条数据等待服务器响应TTFB竟然长达 8.38 秒这简直不能忍。经过一番抽丝剥茧的排查我定位到了从数据库 SQL 索引、大字段去重、字符集冲突到高并发线程安全、字典 N1 循环查库等一连串经典性能瓶颈。最终经过一波针对性重构接口响应时间暴降至 300ms 左右下面我将以个人技术博客的形式详细记录这次“排雷”与“调优”的全过程希望对大家有所启发。 调优前的“四宗罪”我们在哪里卡住了在开始动手改代码前我先在核心业务方法中加了耗时日志埋点经过链路监控找出了以下四个致命性能元凶1. 致命的SELECT DISTINCTlongtext大字段在原来的 MyBatis XML 中SQL 语句类似于下面这样SELECTDISTINCTcs.id,cs.summary,cs.product_photo,...FROMcykc_supply csLEFTJOINcykc_excellent_company cec...为什么会有DISTINCT因为原来的 SQL 使用了LEFT JOIN关联了优秀企业表可能存在一对多的情况导致主表数据行数膨胀出现重复因此前人为了“图省事”直接加了个DISTINCT去重。为什么慢得要命cykc_supply表里的summary供给概述和product_photo产品图片在数据库中是longtext大文本字段。在 MySQL 中如果对结果集使用DISTINCTMySQL 需要对结果集的所有列进行去重和排序Filesort。因为含有longtext大字段MySQL 的内存临时表放不下这么大的数据被迫退化将所有数据写入磁盘在磁盘上创建临时表进行文件排序去重这就好比你搬家时不用轻便的塑料箱非要把几吨重的实木家具在狭窄的过道里搬来搬去做排序速度不慢才怪2. 字符集不一致导致的Illegal mix of collations报错与索引失效两张表做连接ONcs.company_nameCOLLATEutf8mb4_general_cicec.company_nameCOLLATEutf8mb4_general_ci原委主表cykc_supply的公司名字段字符集是utf8mb4_general_ci而优秀企业表cykc_excellent_company的对应字段使用的是utf8mb4_0900_ai_ci。字符集不一致时MySQL 无法直接进行比较直接连表会抛出Illegal mix of collations错误。代价前人为了防止报错在ON连接条件的两边都加上了COLLATE utf8mb4_general_ci做强制转换。这导致了两张表在该列上的所有数据库索引彻底失效MySQL 无法再利用索引快速定位每次 JOIN 都要做全表的字符集强制转换和扫描CPU 直接拉满。3. 循环内的“N1”次数据库字典查询在 Service 层的列表遍历逻辑中for(CykcSupplysupply:cykcSupplyList){if(supply.getAuditStatus()!null){// 每次循环都查一次数据库supply.setAuditStatusName(sysDictDataService.selectDictLabel(audit_status,String.valueOf(supply.getAuditStatus())));}}原本的selectDictLabel直接去查了数据库没走缓存。如果一页返回 10 条数据就会额外执行 10 次 SQL 字典查询若导出 1000 条数据就会执行 1000 次 SQL大量的网络 IO 开销直接把接口响应时间拖到了深渊。4. 线程不安全的重量级 Swing HTML 解析器原先在循环内用Html2Text.getContent(summary)来剔除 HTML 标签其底层使用的是 Swing 的ParserDelegator且把拼接字符串的StringBuffer s声明为了全局静态单例变量。隐患不仅高并发时多线程数据会互相污染A 看到 B 的数据而且在高并发解析超长富文本时很容易引发多线程竞争阻塞和巨大的 GC 压力导致偶发性的卡死。️ 调优实战逐个击破性能瓶颈针对排查出来的“四宗罪”我制定并执行了三层调优方案下面是具体的优化逻辑和代码/SQL对比 突破一SQL 极致重构消除大字段 Filesort 与恢复索引我们优化的核心指导思想是“能不用 JOIN 就不用 JOIN能不用 DISTINCT 就绝对不用 DISTINCT”。既然我们只需要判断company_name是否在优秀企业表cykc_excellent_company中存在完全没必要做大表LEFT JOIN更没必要去重 优化方案移除LEFT JOIN与SELECT DISTINCT消除了大字段在磁盘上做 Filesort 排序的开销。列级 EXISTS 子查询用单行子查询代替 JOIN。显式指定 COLLATE 转换为了解决字符集冲突我们仅在子查询内部的条件中加入COLLATE转换。因为这是单行常量的匹配MySQL 可以完美避开全表扫描继续利用右表的索引 SQL 优化前后对比优化前 ❌selectidselectCykcSupplyListparameterTypeCykcSupplyresultMapCykcSupplyResultSELECT DISTINCT cs.id, cs.supply_type, ..., cs.summary, ..., CASE WHEN cec.id IS NOT NULL THEN 1 ELSE 0 END AS isExcellentCompany FROM cykc_supply cs LEFT JOIN cykc_excellent_company cec ON cs.company_name COLLATE utf8mb4_general_ci cec.company_name COLLATE utf8mb4_general_ciwhere...iftestisExcellentCompany ! null and isExcellentCompany 1and cec.id IS NOT NULL/ififtestisExcellentCompany ! null and isExcellentCompany 0and cec.id IS NULL/if/where/select优化后 ✅selectidselectCykcSupplyListparameterTypeCykcSupplyresultMapCykcSupplyResultSELECT cs.id, cs.supply_type, ..., cs.summary, ...,!-- 用子查询代替 JOIN无 DISTINCT 去重负担 --(SELECT CASE WHEN COUNT(*) 0 THEN 1 ELSE 0 END FROM cykc_excellent_company WHERE company_name COLLATE utf8mb4_general_ci cs.company_name COLLATE utf8mb4_general_ci) AS isExcellentCompany FROM cykc_supply cswhere...!-- 用等价的 EXISTS 替换原 cec.id 过滤使其能够利用索引 --iftestisExcellentCompany ! null and isExcellentCompany 1and EXISTS (SELECT 1 FROM cykc_excellent_company WHERE company_name COLLATE utf8mb4_general_ci cs.company_name COLLATE utf8mb4_general_ci)/ififtestisExcellentCompany ! null and isExcellentCompany 0and NOT EXISTS (SELECT 1 FROM cykc_excellent_company WHERE company_name COLLATE utf8mb4_general_ci cs.company_name COLLATE utf8mb4_general_ci)/if/where/select 突破二全局字典服务接入 Redis 缓存消除 N1 查询其实若依框架本身是支持字典缓存的只是一直空置了。我们修改了公共字典服务的底层逻辑 优化方案优先从 Redis 中获取指定字典类型的列表进行内存匹配。若缓存未命中才降级查询数据库实现“内存级”翻译。 代码对比修改前 ❌OverridepublicStringselectDictLabel(StringdictType,StringdictValue){// 每次调用必查一次数据库returndictDataMapper.selectDictLabel(dictType,dictValue);}修改后 ✅OverridepublicStringselectDictLabel(StringdictType,StringdictValue){// 1. 优先从 Redis 缓存中获取该类别的全部字典ListSysDictDatadatasDictUtils.getDictCache(dictType);if(StringUtils.isNotNull(datas)){// 2. 缓存在内存中匹配0次数据库IOfor(SysDictDatad:datas){if(dictValue.equals(d.getDictValue())){returnd.getDictLabel();}}}// 3. 缓存没有才查库returndictDataMapper.selectDictLabel(dictType,dictValue);} 突破三轻量级正则清理 HTML 标签消除 Swing 重度解析我们舍弃了原本臃肿不安全的 Swing 文本解析方案改为采用无状态的正则表达式进行 HTML 标签的剥离。 优化方案使用极其轻量的正则replaceAll([^]*, )将 HTML 标签过滤耗时从百毫秒级降至0.1 毫秒级且完全无状态不存在任何多线程安全隐患。 代码对比修改前 ❌publicstaticStringgetContent(Stringstr){try{Html2TextparsernewHtml2Text();// 每次实例化调用 DOM 树解析parser.parse(str);returnparser.getText();}catch(IOExceptione){e.printStackTrace();}return;}修改后 ✅publicstaticStringgetContent(Stringstr){if(StringUtils.isEmpty(str)){return;}// 用正则代替 Swing DOM 解析耗时直接可以忽略不计Stringtxtstr.replaceAll([^]*,);txttxt.replaceAll(nbsp;, ).replaceAll(amp;,).replaceAll(lt;,).replaceAll(gt;,);returntxt.trim();} 调优战果8.38s 降至 300ms优化完成并成功编译发布后我们再次对列表接口进行了性能测试效果简直立竿见影原接口响应时间8380 ms优化后响应时间320 ms优化幅度响应速度提升了 26 倍日志埋点输出selectCykcSupplyList SQL 查询耗时: 110 ms, 查询条数: 10 selectCykcSupplyList 循环处理耗时: 4 ms原本执行一次需要数秒的 SQL重构后仅需110毫秒即可完成而原本需要几秒的循环解析和字典翻译如今更是缩减到了近乎可以忽略不计的4毫秒 个人成长心得与感悟这次性能调优给了我几点深刻的技术感悟在此分享给大家SELECT DISTINCT是一把双刃剑在写 SQL 时如果发现数据重复千万不要无脑加上DISTINCT解决。一定要先排查为什么会产生重复行。尤其是当 SELECT 列中包含大文本字段如TEXT/LONGTEXT时DISTINCT会强迫 MySQL 进行昂贵的磁盘文件排序这是非常致命的瓶颈。慎用COLLATE连接条件表设计时应尽量保持全库中字段字符集排序规则的一致性。在ON连接条件中使用COLLATE会直接废掉数据库的索引。如果历史包袱较重也应该优先考虑改写为子查询限制其对主查询索引的影响。消灭循环查库的“坏味道”在循环体中执行 SQL 查询N1问题是初学者最容易写出的代码。我们要牢记“批量加载”或者“合理利用 Redis 缓存”的原则。把 N 次查库降为内存级匹配接口性能就能产生质的飞跃。博客后记写好每一行代码不仅是对系统性能的敬畏也是提升我们自身工程素养的基石。希望这篇调优笔记对你有所启发。如果你有更好的优化想法欢迎在评论区一起讨论交流

相关推荐

OpenGL的帧缓冲和纹理使用逻辑
OpenGL的帧缓冲和纹理使用逻辑

一、先简单说一下OpenGL的纹理使用本质上还是状态机,要把加载的图片放到插槽里面,然后着色器通过访问插槽去拿到纹理。因此纹理从加载到使用需要经历如下步骤(1-5可以写成一个纹理加载函数):1.先绑定纹理,告诉opengl接下来的操作在… · 2026/9/27 22:33:47

北京车展和上海车展将无缝衔接
北京车展和上海车展将无缝衔接

问差友们一个问题啊,你们觉得今年的前五个月,国内的车企们一共发布了多少台新车?给一个提示哈,在2015 年新能源汽车还没有怎么出现的时候,整个国内市场发布的新车数量是 84 台( 仅计算开了发布会的&#xf… · 2026/9/21 8:41:42

Mysql数据语法(基础语法)
Mysql数据语法(基础语法)

一、增 1.创建数据库 create database 库名; 2.创建新表 create table 表名; 3.新增数据 列表依次新增 insert into 表名 values (值1,值2...); 指定字段新增 insert into 表名(列1,列2,列3)values(值1,值2&#xff0c… · 2026/9/20 7:00:49

重庆自动seo避坑指南3个关键注意事项助新手零代码建站
重庆自动seo避坑指南3个关键注意事项助新手零代码建站

重庆自动seo避坑指南3个关键注意事项助新手零代码建站 自己不会代码想做网站,最怕的就是被“自动SEO”这种词忽悠得云里雾里,最后钱花了,流量没来。很多重庆的中小企业老板,特别是做本地生活服务或实体贸易的,手里没技术团队,只想找个省事的路子… · 2026/9/27 22:33:59

OpenClaw部署教程完整版:用TaoToken统一API Key的最简配置流程
OpenClaw部署教程完整版:用TaoToken统一API Key的最简配置流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 22:33:53

用 Microsoft Agent Framework 构建 SubAgent(Multi-Agent):config.toml 骨架与验证清单
用 Microsoft Agent Framework 构建 SubAgent(Multi-Agent):config.toml 骨架与验证清单

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 22:33:53

目前热门的IP驱动产业新场景新工具哪家专业
目前热门的IP驱动产业新场景新工具哪家专业

开篇:定下基调测评背景与目的近年来IP价值从单一内容输出向全产业场景渗透,数字工具作为IP落地的核心载体,正在重构实体经营、康养服务、副业创业等多赛道的协作模式。本次测评聚焦IP驱动产业落地的实际效能,从真实落地案例、用户… · 2026/9/27 22:33:53

AIGC智能编程实战:大模型代码助手巧学巧用与TaoToken配置指南
AIGC智能编程实战:大模型代码助手巧学巧用与TaoToken配置指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 22:33:47

OpenAI自研芯片降本50%背后:用TaoToken统一Key打通AI工具链配置
OpenAI自研芯片降本50%背后:用TaoToken统一Key打通AI工具链配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 22:33:47

MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现

简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01

汕头网站建设制作厂家避坑指南:5大注意事项救急
汕头网站建设制作厂家避坑指南:5大注意事项救急

汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01

多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习

简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01

MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现

简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01

汕头网站建设制作厂家避坑指南:5大注意事项救急
汕头网站建设制作厂家避坑指南:5大注意事项救急

汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01

多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习

简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码