并发上报卡顿如何排查?从TCP重传到批量写入的吞吐优化实战

发布时间:2026/9/12 16:58:02
并发上报卡顿如何排查?从TCP重传到批量写入的吞吐优化实战 做一个机房环境监控的运维最怕半夜手机响。上周我这边就碰上一档子事现场一百个POE供电的温湿度变送器数据全卡在服务端上不来了。监控大屏停留在十分钟前的数值告警短信倒是没闲着一条高温告警五分钟内推了三遍。赶到机房看交换机所有端口指示灯都在闪设备一台没掉线服务端CPU占用率不到10%内存还剩三个G怎么看都不像资源不够。但数据就是不动。这种问题最磨人。表面看起来一切正常实际上整条链路已经堵死了。排查到最后问题不出在网络也不出在设备而是出在数据从接收到落库的这条处理链路上。这篇就把整个排查过程和最终的吞吐优化方案完整记录下来给同样用POE温湿度变送器做点位监控的朋友一个参考。1. 现场还原一百个点位同时上报卡顿到底卡在哪1.1 项目基本盘与卡顿表象项目情况不复杂五个机房每个机房部署二十个POE供电的温湿度变送器一共一百个点位。这些变送器通过网线既取电又传数据POE供电省去了单独拉电源线的麻烦设备本身用TCP长连接主动把温湿度数据上报到采集服务器报文格式是自定义的十六进制帧包含设备ID、温度、湿度、信号强度和UTC时间戳一帧下来不到80个字节。设计上报周期是5秒一次一百个设备理论上峰值就是每秒20条数据按80字节一帧算也才1.6KB/s的流量。这个量级放在任何链路上都算不上压力。数据库用的MySQL采集服务端是单机部署的Java进程监听一个TCP端口等待设备连接。环境监控大屏每两秒刷新一次从MySQL查最新数据渲染曲线。正常情况下一百个点位全部在线数据延迟不超过2秒。出问题那天延迟从2秒逐渐拉大到10分钟大屏上的温度曲线出现明显的断层段告警模块同一时间收到几十条重复告警。这是最典型的吞吐不足表现——数据一直在产生但处理速度跟不上积压越来越严重。1.2 一开始最误导人的三个判断接手排查时我先做了三个常规检查全被表象带偏了。第一是怀疑网络拥塞。但流量才1.6KB/s别说千兆交换机了就是十年前的老百兆交换机都绰绰有余。抓包看到的是TCP层大量重传看起来像网络问题实际上只是结果不是原因。第二是怀疑设备离线。用批量ping工具扫了一遍一百个设备全部通TCP连接也都维持在ESTABLISHED状态。第三是怀疑数据库表满了或者磁盘满了查了一圈表空间充裕磁盘剩余50%以上。三个常见怀疑都被排除后才意识到问题出在应用处理链路上。这里也想提醒一句遇到并发上报卡顿先别急着怪网络和硬件把接收和处理拆开看大多数瓶颈都在处理侧。2. 抓包定位从网络层到应用层的完整排查链2.1 交换机镜像端口抓包先排除网络嫌疑为了排除交换机层面的问题我在接入交换机上配置了一个镜像端口把连接采集服务器的那个口的所有流量镜像出来用Wireshark抓了十分钟。抓包结果有几个关键信息。第一个是TCP重传比例高得异常大概有4.7%的报文触发了重传正常局域网环境这个数字应该在0.1%以下。第二个是重传的报文主要集中在服务端返回的ACK包上也就是说接收方向处理不过来内核缓冲区满了开始丢包TCP协议栈只能重传。第三个细节是应用层数据帧本身没有丢只是被延迟处理了。这个结果把网络层面的嫌疑基本洗清了。重传是因为服务端应用层消费得太慢而不是链路质量差。交换机上跑的POE供电和普通数据转发没有直接关联POE只是供电方式和TCP传输路径是两个维度的事情。2.2 服务端日志里的积压证据接下来看采集服务端的日志。这个服务的逻辑比较简单每个设备连接对应一个独立的接收线程线程循环读取socket数据解析出温湿度之后单条执行INSERT语句写入MySQL同时在控制台和日志文件里各打一条日志记录。日志文件暴露了问题的最直接证据接收线程日志的编号和写入线程的编号对不上。接收线程每秒确实能收到20条左右的数据但数据库确认写入的行数远远跟不上积压任务数一路攀升。日志还暴露了另一个隐藏问题——每条数据打两行日志一百个点每秒产生40行日志输出磁盘IO在中高负载下持续饱和日志写入本身反过来拖慢了主流程。服务端的CPU不到10%但磁盘的await时间已经飙升到几百毫秒。这里其实出现了一个典型误区接收快、解析快不代表整个服务快。接收是一个环节日志是一个环节数据库写入是另一个环节三个环节串行绑定任何一个环节卡顿都会把前面的环节全部拖住。2.3 数据库慢查询藏在最底层的真凶打开MySQL慢查询日志真相大白。INSERT语句的平均执行时间高达120毫秒有些甚至跑到300毫秒以上。要知道这只是一条几十个字节的数据插入正常情况下应该低于5毫秒。慢的原因有三层叠加。第一层是单条提交每条数据一个INSERT每个事务都要等待磁盘fsync。第二层是innodb_flush_log_at_trx_commit参数被设成了1意味着每次事务提交都要把redo log刷到磁盘。磁盘的随机写性能本身就有限叠加频繁提交性能直接崩了。第三层是这张温湿度记录表上还挂了一个触发器每次插入都会触发一次实时计算去更新统计表进一步拉长了单条写入的耗时。单条INSERT耗时120毫秒100个设备同时上报的瞬间假设有60个请求同时到达排队等待时间就是60×120毫秒等于7.2秒再加上后续请求源源不断积压只会越来越严重。这就是小流量大事故的底层机制。2.4 为什么小流量会演变成大事故重传恶性循环如果只是每秒20条稳定写入即使单条120毫秒也能慢慢消化。真正让问题失控的是恶性循环接收线程积压导致内核TCP接收缓冲区满TCP协议栈丢弃新到的报文设备端等不到ACK就触发重传。重传的报文再次进入接收队列再次排到队尾进一步加剧处理延迟。设备端还有个重传超时机制重传几次还没收到ACK就直接断开连接重连重连后又会把积压的数据全部补报一遍。所以抓包看到的重传包根本不是网络问题而是应用层处理速度跟不上之后TCP协议栈在被动保护自己。这种恶性循环一旦形成即使数据库瞬间恢复正常也要花很长时间才能把积压队列消化完。这也解释了为什么大屏上的数据会卡在十分钟前——积压从最初的一点点通过重传和补报被持续放大。3. 吞吐瓶颈的本质并发上报为何会卡顿3.1 并发数和吞吐量是两码事一百个设备并发上报听起来很吓人实际上并发上报和吞吐量是两个维度的问题。并发说的是同一时刻有多少请求在飞行吞吐说的是单位时间内系统能处理多少请求。用收费站来类比一百辆车同时开到收费站这是并发100收费站一分钟能放行多少辆车这是吞吐。如果只有一个收费员每辆车要收三分钟费那并发一百辆车的结果就是后面排成长龙过站速度惨不忍睹。温湿度监控系统也一样。100个设备5秒上报一次看起来平均负载不高但设备的时间戳没有对齐机制重启后或者断网重连时几十个设备会在同一秒内同时上报瞬时并发可能冲到上百条。系统处理不过来这些请求就全部堵在队列里表现为数据延迟和告警风暴。3.2 链路里最慢的环节决定整体效率数据从传感器到屏幕要经过四个环节设备生成数据帧、网线传输到采集服务端、服务端解析、数据库落盘。四个环节的处理速度差异巨大。设备的探测周期和报文发送是毫秒级网络传输在局域网内是亚毫秒级服务端接收和解析一个80字节的报文也只需要微秒级。但数据库单条写入在最差情况下需要120毫秒。这个数字意味着什么接收环节一秒钟能处理上万条报文写入环节一秒钟只能处理不到十条。木桶效应在这里体现得淋漓尽致整条链路的实际吞吐由最慢的环节决定也就是每秒不到10条远远低于设备产生数据的速度。而且这四个环节之间如果用了同步阻塞的串行模型慢环节还会反向拖累快环节。接收线程执行完INSERT之前不会去读下一个报文socket缓冲区里的数据堆积最终导致TCP层丢包重传。这就是典型的快车道被慢车道堵死。3.3 同步阻塞模型是怎么把卡顿放大的这个Java采集服务的模型很简单一个设备连接对应一个线程线程循环读数据、写数据库再读下一条。每个设备线程都在自己的生命周期里独立工作看起来互不干扰实际上所有线程最后都挤在数据库写入这一个瓶颈点上。更糟的是数据库连接数是有限的。连接池默认配置只有20个连接100个设备线程同时需要写库时大部分线程都阻塞在等待获取数据库连接上。线程拿不到连接就占着socket不读取数据socket缓冲区填满后TCP对端就感知到了丢包和重传。整个系统的瓶颈从数据库开始沿着连接池、线程、socket一层层向上传导把原本不成问题的接收环节也拖垮了。这里有一个很重要的认知只看CPU和内存使用率判断系统是否健康在高并发场景下会有严重的误导性。瓶颈在IO和锁等待时CPU利用率会很低但系统已经卡死了。4. 优化落地一个网关角色和批量写入改造4.1 调整架构接收与写入解耦方案其实不复杂核心就一句话把接收数据和写入数据库从同一个线程里拆开中间加一个内存队列做缓冲。调整后的模型长这样接收线程只负责从socket读取数据、解析出温湿度字段然后把数据对象扔进内存队列立刻返回继续读下一个报文。这个过程的耗时是微秒级的一百个设备并发上报也完全接得住。另外单独启动两个写入线程从一个内存队列里批量取数据攒批后执行批量INSERT。这样一来快环节和慢环节之间彻底断开了直接绑定快环节不会因为慢环节卡顿而停滞。内存队列选型上用的是ArrayBlockingQueue容量设了10000。按每秒20条正常流量算相当于能缓冲500秒的数据即使数据库短时间夯住队列也有足够的缓冲空间让接收线程不丢数据。写入线程数量设两个而非一个是为了避免单个写入线程在批量执行期间队列积压速度超过消费速度。4.2 攒批策略与关键参数批量写入不是简单地攒一批执行一批要设计好触发策略否则会出现两种情况攒得很慢的时候延迟变大攒得太快的时候批量效果不明显。我用的策略是双条件触发队列里积压了50条数据或者距离上一次批量写入已经超过500毫秒两个条件任何一个先满足就触发一次批量INSERT。这样在正常流量下最多500毫秒刷一批大屏刷新时能看到的数据延迟控制在1秒以内在突发流量下队列快速积压到50条阈值批量写入立即执行吞吐最大化。批量INSERT的SQL长这样INSERT INTO temp_humidity_log (device_id, temperature, humidity, signal_strength, record_time) VALUES (DEV001, 24.5, 45.2, -42, 2025-01-10 14:00:01.125), (DEV002, 25.1, 44.8, -38, 2025-01-10 14:00:01.236), (DEV003, 23.9, 46.1, -51, 2025-01-10 14:00:01.351), -- 省略后续若干条 (DEV100, 24.8, 45.0, -44, 2025-01-10 14:00:04.872);实测批量50条的INSERT总体耗时在200毫秒左右平均每条约4毫秒相比之前的单条120毫秒吞吐提升了30倍。如果把批量大小加到500条整体耗时也只到800毫秒左右均摊成本更低但延迟会变大而且网络抖动时积压数据集中刷盘对底层存储的压力很不友好。50条是我这边压测下来的甜点值。4.3 数据库和日志层面的调优跟批量写入配套的还有几个辅助优化。日志输出从每条数据打两行改成每5秒打一行统计摘要格式类似5秒内收到100条批量写入2批失败0条。这一条改动把磁盘IO的占用降了90%以上也直接消除了日志写盘和数据库写盘之间的资源竞争。数据库参数方面innodb_flush_log_at_trx_commit从1改成了2。这个参数的作用是控制redo log的刷盘时机1表示每次事务提交都刷盘数据安全性最高2表示每秒刷一次盘性能更好安全性略降。温湿度监控的数据丢了1秒也不是问题为了吞吐把这里调到2完全合理。sync_binlog如果开了也建议调成0或者每N次事务刷一次原理类似。不过注意如果是金融系统或者账务数据这两个参数不建议乱动安全等级要求完全不同。触发器也做了调整。原本每条INSERT触发一次实时统计更新改成由批量写入完成后单独执行一条汇总UPDATE把统计粒度从每帧触发降为每批触发。4.4 设备端错峰上报从源头削峰服务端优化做完之后我还顺手做了一件事把一百个点位的上报周期从统一的5秒改成了5秒加随机偏移。每个设备在上报周期里加一个0到1000毫秒的随机数让一百个设备的实际上报时刻尽量错开而不是整齐划一地同时发。这个改动看起来不起眼实际效果很明显。原来设备断网重连后一百个设备会同时建立TCP连接并立刻补报积压数据瞬时并发量冲到几百条。加了随机偏移之后重连补报的流量被摊开到1秒内服务端的瞬时压力降了一个数量级。TCP重传率从4.7%降到了0.1%以下几乎看不见重传了。设备端偏移时间不能加太长否则会影响数据实时性。1000毫秒以内的随机偏移对5秒周期来说最大延迟影响是20%大屏上2秒刷新读取最新数据时感知不到这个误差。5. 压测验证与避坑记录5.1 压测方案与数据对比优化做完不能嘴上说好了要拿数据说话。我用JMeter搭了一个压测环境模拟100个虚拟设备并发上报。JMeter线程组设置100个线程每个线程模拟一个设备通过TCP Sampler发送自定义的温湿度数据帧发送间隔用Constant Timer控制在5秒循环跑30分钟同时监控服务端的接收计数和MySQL里的实际落库行数。压测结果对比如下指标优化前优化后有效写入吞吐约8条/秒持续积压稳定500条/秒以上端到端数据延迟3分钟至10分钟1秒以内TCP重传率4.7%0.1%以下数据库单条均摊耗时约120ms/条约4ms/条CPU占用率8%空闲但卡顿12%忙但稳定这个数据说明一个问题同样的设备和网络环境纯粹靠调整服务端的数据处理路径就能把整体吞吐提升60倍以上。卡顿的根源确实不在设备和网络而在应用侧对并发流量缺乏缓冲和批量处理能力。5.2 三个坑每个都值得单独说第一个坑是批量大小贪大。一开始我把攒批阈值调到了500条想着吞吐还能再翻几倍。结果吞吐确实上去了但延迟波动明显变大数据库偶尔会出现一次集中刷盘导致的抖动同时监控大屏上的曲线出现阶梯式更新。50条一批就是当前环境下的平衡点别盲目追求大。第二个坑是POE供电不稳定导致的诡异故障。有一批设备在优化后依然频繁掉线重连抓包看是设备主动发的FIN包但没有任何规律。最后排查到供电环节非标POE供电的线路长度超过了90米电压衰减严重设备在发送数据瞬间电流需求增大电压跌落触发设备低电压保护重启。换了一个功率余量更大的POE交换机后问题消失。软件优化解决不了硬件供电问题这个坑必须单独列出来让后来人注意。第三个坑是优化过程中差点把自己坑了——一把日志改成摘要格式后日志里看不到单台设备的明细了后续排查一个温度误报时花了很长时间。后来我加了一个可配置的参数默认输出摘要日志需要排查单台设备时打开DEBUG级别输出。可观测性是最后一道安全网优化时千万别把观测手段一并优化掉了。5.3 可以直接抄走的优化清单总结一下这套方案按优先级排序接收与写入解耦中间加内存队列缓冲防止快环节被慢环节阻塞拖死。批量写入攒批触发双条件策略积压量或时间窗口同步把单条INSERT改为多值INSERT。日志降频从逐条打印改为周期汇总避免日志IO与数据库IO互相抢占资源。数据库刷盘参数按业务容忍度调整温湿度监控这类数据丢失容忍度高的场景可以放弃每次事务都fsync。设备端上报时间加随机偏移从源头摊平突发流量减少瞬时并发峰值。压测验证压测环境要尽量模拟真实上报分布而不是均匀稳定地发数据否则测不出真实的积压情况。这次折腾下来最大的体会是并发上报的场景里真正决定系统能不能撑住的不是接收端有多快而是最慢的那个环节被缓冲了多少。给慢环节套一层队列让快环节不被拖住收益比给服务器加CPU大得多。优化完之后这套系统已经稳定跑了三周一百个点位的数据延迟始终控制在1秒以内至少半夜不用被电话吵醒了。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询