背景:装了插件的 IDEA 卡到不能用
团队自研的 IDEA 插件更新后,出现了一个很诡异的症状:什么都没做,只是打开了几个文件,IDEA 就卡得不行。CPU 风扇狂转、界面掉帧、时不时冻结几秒。
比”卡”更有价值的是手里的 GC 数据。连续抓了几轮 jstat -gcutil:
1 | S0 S1 E O M CCS YGC YGCT FGC FGCT GCT |
只看数字就能拆出三层信号。这篇文章完整复盘这次排查:从 jstat 解读、到沿执行路径找代码证据、到三类修复的设计与验证。
团队自研的 IDEA 插件更新后,出现了一个很诡异的症状:什么都没做,只是打开了几个文件,IDEA 就卡得不行。CPU 风扇狂转、界面掉帧、时不时冻结几秒。
比”卡”更有价值的是手里的 GC 数据。连续抓了几轮 jstat -gcutil:
1 | S0 S1 E O M CCS YGC YGCT FGC FGCT GCT |
只看数字就能拆出三层信号。这篇文章完整复盘这次排查:从 jstat 解读、到沿执行路径找代码证据、到三类修复的设计与验证。
老 MES 系统里”导出”大概是最容易被低估的功能:早期数据量小,一个同步接口把数据查出来写成 Excel 直接回传,皆大欢喜。等到单表日志类数据涨到千万行、报表动辄几十万行时,这条链路开始连环爆雷。本文记录一次完整的导出链路改造:从同步超时丢文件,到「提交即返回 + 游标流式读取 + 异步写入 + 桌面通知取件」的全套架构,包括关键实现细节、踩过的每一个坑,以及清楚留着的优化债。
改造前的链路是典型的”一把梭”:
1 | 用户点导出 → 请求线程分页循环查全量 → 攒成大 List → 写 Excel → HTTP 响应回传 |
数据量上来后暴露的问题按出现频率排序:
List<Map> + 写缓冲),几个人并发导出大报表,堆直接见顶;期间打过一层补丁:同步导出超时后转后台继续写,完成后上传文件、发通知。这解决了一部分问题,但”用户先同步等 30 秒”的体验和分页翻页式读取的额外开销还在。于是有了这次彻底的改造。
仅修改某个类的实现逻辑或修复bug而不重新编译构建整个项目时,可以使用命令行工具手动替换class文件。


1 | pv dengqimes_20250519_144357.sql | mysql --database=dengqimes-test --user=zy_mes -pr4UQBwBV --host=127.0.0.1 --port=3306 --default-character-set=utf8mb4 |
使用 pv | mysql的组合,老版mysql在shell命令处理参数时,会忽略 dengqimes-test 中的 -,导致实际上mysql将备份还原到了数据库 dengqimes 上。而正式环境和测试环境数据库名恰好仅用-加后缀进行区分。

| 客户端请求路径 | 后端接收路径 |
|---|---|
https://域名/J5BfNtYvHW/api |
http://后端:54321/J5BfNtYvHW/api |
https://域名/api |
http://后端:54321/J5BfNtYvHW/api |
要实现 所有客户端请求路径 最终在后端统一映射到 /J5BfNtYvHW/api,可以通过以下两种方案实现:
1 | server { |
路径映射效果:
| 客户端请求路径 | 后端接收路径 |
|---|---|
https://域名/J5BfNtYvHW/api |
http://后端:54321/J5BfNtYvHW/api |
https://域名/api |
http://后端:54321/J5BfNtYvHW/api |
1 | server { |
路径映射效果
| 客户端请求路径 | 后端接收路径 |
|---|---|
https://域名/J5BfNtYvHW/api |
http://后端:54321/J5BfNtYvHW/api |
https://域名/api |
http://后端:54321/J5BfNtYvHW/api |

AUTO_INCREMENT字段1 | select max(id) from biz_wms_delivery_order_barcode; |
才几万行数据,表ID已经增长到快100万了
1 | <update id="insertDeliveryOrder" parameterType="Map"> |
三个表同样的现象,自增ID不连续。
使用 INSERT INTO ... ON DUPLICATE KEY UPDATE 或 INSERT IGNORE INTO 语句的坑。
使用 INSERT INTO ... ON DUPLICATE KEY UPDATE 语句:
即使在唯一键冲突时执行更新操作,没有实际插入数据库,AUTO_INCREMENT 计数器仍会递增,导致ID值跳跃。
1 | insert into biz_wms_delivery_order( |
尤其是在批量操作的时候,假设批量插入100条数据,其中5条插入成功,95条已存在执行更新操作,自增主键ID会在此次语句执行完后增大100而不是5,即使实际上只插入了5条新数据。
可以手动多执行几次同样的SQL,通过 show create table 语句查看表的 AUTO_INCREMENT 属性验证。
使用 INSERT IGNORE INTO 语句,也是一样。假设批量插入100条数据,其中5条插入成功,另外95条由于主键冲突被忽略,实际 AUTO_INCREMENT 增大了100。
表结构,查看 AUTO_INCREMENT
1 | show create table biz_wms_delivery_order_barcode; |
网上查询可知以上是mysql默认的自增ID分配策略导致的问题,会直接预先分配ID,导致
AUTO_INCREMENT增长。虽然调整innodb_autoinc_lock_mode配置,可以修改该策略,但是修改该参数会影响并发性能。
因为需要定时从SRM同步,每次执行 INSERT INTO ... ON DUPLICATE KEY UPDATE 会有大量已存在的数据需要更新,导致ID跳跃的非常大。
最终还是要修改业务表的插入逻辑,不使用 INSERT INTO ... ON DUPLICATE KEY UPDATE ,将其拆分为插入和更新两部分。
方案: 在插入前先执行查询,判断记录是否存在;如果存在,则执行更新;如果不存在,则执行插入。
优点: 避免了自增ID的跳跃。
缺点: 增加了查询和判断的开销,增加了代码,可能影响性能,但影响不大可以接受。
1 | select table_name as '表名', |
使用 --ignore-table 选项
1 | mysqldump -uzy_mes -p12345 alpsmes \ |
使用 --no-data 选项
1 | mysqldump -uzy_mes -p122345 alpsmes \ |
1 | mysqldump -uzy_mes -p12345 --routines --no-data --databases dengqimes > /tmp/dengqimes_structure.sql |
使用 --where 选项
备份时只备份数据不备份表结构
使用 --no-create-info 选项
1 | mysqldump -uzy_mes -p122345 dengqimes biz_technics_opc_quality --where="factoryid=3 and deviceid between 88 and 91" --no-create-info > output_technics_91.sql |
mysqldump备份的历史数据库文件(.sql),不想恢复整个数据库,只想取出其中一张表
原理是使用 sed 工具
1 | sed -n -e '/CREATE TABLE `biz_order_child`/,/UNLOCK TABLES/p' dengqimes20240118000001.sql > orderchild.sql |
或者
1 | sed -n -e '/Table structure for.*`biz_order_child/,/Table structure for/p' dengqimes20250207000001.sql > biz_order_child.sql |
或者
1 | sed -n -e '/DROP TABLE IF EXISTS `biz_product`/,/UNLOCK TABLES/p' cloudmes20241121000001.sql > biz_product.sql |
mysql还原备份的sql时,可能由于字符集编码和转义问题会报错
1 | mysql --database=dengqimesprod --user=root --host=127.0.0.1 --port=3306 < C:\Users\zhiyin\Downloads\dengqimes20240311.sql |
需要指定字符集和数据库的字符集一样
1 | mysql --database=dengqimes --user=root -p --host=127.0.0.1 --port=3306 --default-character-set=utf8mb4 < C:\Users\zhiyin\Downloads\dengqimes20240413.sql |
记录一下使用 Node.js 后台读取串口数据并通过 WebSocket 实时显示在网页上的方法,可以使用以下步骤:
以下是一个简单的示例:
1 | // Node.js 后台代码 |
注意:在这个示例代码中,我们假设“数据格式为文本字符,形如0 00 xxx 00,获取其中的xxx”