
1. “同配置不同命”一场让我怀疑测试脚本的48核对比先说结论我见过太多人把数据库一体机的性能直接等同于“CPU核数内存大小NVMe数量”然后拿着配置单去比价。这种思维不能说错但在真实业务里会踩大坑。最典型的例子就是这次要聊的两台都是48核配置的数据库一体机硬件参数几乎一致可一跑交易负载TPS直接差了一倍。当时的情景是这样的。一台一体机是客户A在用的型号属于某厂家的“高性价比”系列48核、256GB内存、全NVMe盘、万兆网络数据库是同一版本甚至连操作系统小版本都一样。另一台是客户B在用的同样是48核、256GB内存、全NVMe盘。对比之前我看配置单的反应是这两台机器跑同一个交易场景性能差距应该不会超过5%。结果压力一打上去就懵了客户A的机器TPS稳定在21000左右客户B的机器能跑到43000多误差范围内的确差了一倍。第一反应是怀疑测试脚本出了问题或者是JMeter本身没配好。可把两边的压测脚本拉出来对比交易比例、并发线程数、数据量、提交方式全都一样。又怀疑是数据库参数不一致结果show variables里的核心参数也都差不多。最后我把排查范围扩大到BIOS设置、NUMA拓扑、网卡中断绑定、I/O调度策略这些“配置单上不写”的层面才算找到了真正的差异。先说清一个概念这里的TPS指的是数据库每秒钟能处理的事务数。事务是一组逻辑上完整的数据库操作比如“查询订单扣减库存生成流水”算一个事务执行完要么全部成功要么全部回滚。TPS是数据库性能最核心的指标之一但它并不只取决于CPU有多少核。CPU核数决定的是“理论上限”而操作系统、数据库内核、存储协议、网络链路这几层能不能把CPU喂饱才决定你实际能拿到多少。把48核机器跑成只有一半性能恰恰是因为“硬”配置没问题但“软”的那套协同机制没用对。这篇文章就把这些藏在后台的协同细节掰开揉碎讲一遍顺便聊聊怎么用JMeter把真实TPS测准以及在实际项目中怎么对某个交易做TPS限流。2. 软硬协同的胜负手一体机的性能藏在配置单之外2.1 CPU与NUMA让每个核守好自己的“本地内存”服务器是双路架构时每一颗CPU都有自己的内存控制器访问自己直连的内存最快访问另一颗CPU的内存要跨QPI/UPI总线延迟会明显变高。这就是NUMA非一致内存访问的基本逻辑。数据库一体机如果没做NUMA优化线程可能被操作系统随便调度到任意CPU上内存却分配在另一个CPU的远端结果就是大量访问绕远路CPU忙等内存回包TPS自然上不去。我在那次对比测试里看到的最直观差异就是客户B那台机器数据库进程的CPU绑定和内存分配全部固定在Socket 0上客户A那台则是数据库线程在两个Socket之间来回漂移。用numastat看客户A的跨节点内存访问占比明显偏高CPU的系统态消耗也多出一截。这不是厂商给的文档里会写的东西但你一旦理解NUMA对数据库吞吐的影响就会明白一体机的“软”调优有多重要。实际操作上最常见的做法是将数据库实例通过numactl --cpunodebind绑定到一组固定的CPU核心上再配合taskset把主要线程固定好。如果数据库本身支持线程肥化比如每个CPU核心对应一个worker线程那还要确保线程不被随意迁移。另外需要留意的是超线程。数据库这类高负载业务很多时候关闭超线程比开着更稳因为超线程共享执行单元两个逻辑核抢资源反而可能导致延迟抖动。2.2 网卡与中断绑定别让一个CPU成为“快递员瓶颈”压力测试时每秒几万个请求从JMeter发到数据库一体机每个数据包到达网卡都会触发中断。Linux默认情况下中断可能被分配到任意CPU上处理如果各网卡队列和各CPU之间的映射不合理个别CPU会被大量中断淹没别的CPU却闲着。数据库的SQL处理本身要消耗CPU计算资源如果CPU时间被频繁抢走去处理中断TPS自然受影响。好的软硬协同是每张网卡支持多队列RSS每个队列绑定一个CPU核心并且这个核心尽量和数据库实例处理网络线程的核分开或者干脆让同一批核同时处理数据接收和SQL解析只要绑定的核不会因为中断处理而“饿死”就行。我记得那个测试里客户A的机器没有做网卡队列和CPU的亲和性设置所有网络中断都集中到了CPU 0和CPU 1上客户B则把48个核里面分离出4个核专门跑中断数据库主体在另外44个核上。就这一个调整TPS就可能差出去20%。2.3 内存与缓冲池在“快”和“稳”之间找平衡数据库一体机通常预装了大量内存但内存快不代表命中率高。数据库的Buffer Pool是性能和磁盘I/O之间的缓冲层Buffer Pool越大、命中率越高磁盘访问就越少。可底层还有个细节容易被忽略TLB页表缓存。如果操作系统默认使用4KB小页一张大表被载入内存时页表条目数量巨大TLB缓存经常失效CPU每次访问内存都要多查好几轮页表。开启HugePages大页后2MB大页对应的页表条目少得多TLB命中率提升CPU访问内存的路径被缩短。不少一体机出厂时已经默认开启了大页但如果你在自己搭建的环境里复现一定要记得把数据库进程的内存锁定、关闭transparent_hugepage的defrag模式否则系统后台整理内存时会带来不小的抖动。顺带提醒hugepages配置过大或过小都会出问题一般按数据库SGA/PGA总需求预留再用cat /proc/meminfo核对HugePages_Free的值。2.4 存储与落盘事务提交的“最后一道关卡”数据库事务提交要保证持久性也就是redo/undo日志要落盘。传统机械盘时代每次commit都调用一次fsync这几乎是性能瓶颈的根源。现在NVMe盘虽然单次延迟已经很低但同样存在“日志组提交”的协同问题。一体机如果支持组提交group commit多个事务在极短时间窗口里共享一次fsync吞吐就会明显上升。我之前对比那两台机器时发现客户A的innodb_flush_log_at_trx_commit配置与客户B相同但I/O调度器不一样。客户A使用的是通用cfq调度而NVM Express盘其实用none或noop更合适。这个调整小但批量提交场景下对TPS稳定性和尾部延迟影响是实实在在的。更专业的一体机还会用类似io_uring的异步I/O框架让数据库日志写入不阻塞用户态线程减少CPU的上下文切换开销。2.5 数据库内核参数与“出厂预调优”的价值一体机与普通服务器的本质区别不只是硬件选型还包括厂商在出厂前针对这套硬件做过的系统性参数优化。比如数据库实例的并发线程数、锁等待超时、日志缓冲大小、网络连接超时、优化器的统计信息策略等等——这些参数在通用服务器上通常由DBA根据经验去调在一体机上则被集成到一套“交付基线”里。“高性价比”一体机之所以能维持相对较低的价格往往是把钱花在了刀刃上采用中等偏上的CPU和NVMe省掉没必要的高端外设但把所有可能影响数据库性能的操作系统级和数据库级参数都在出厂前固化好了。所以你在JD上买一台同样配件的“裸服务器”跑出来的性能很可能不如这一体机。这不是玄学是软件调优的累积差距。下面用一张表概括常见的软硬协同维度方便你在做选型或自建时逐项核对协同层面关键点常见问题合理调优方向CPU调度NUMA绑定、超线程线程跨Socket迁移numactl/taskset绑定必要时关HT网络多队列、RSS、中断亲和中断集中到少数CPU每队列绑一个独立CPU核心内存大页、内存锁定TLB频繁失效开启HugePages关闭THP defrag存储I/O调度、组提交、异步I/Ofsync阻塞用户态线程NVMe用none开启日志组提交数据库并发模型、日志参数、统计信息出厂基线缺失按硬件规模预调优形成基线模板3. TPS虚高的真相为什么JMeter跑出的漂亮数字不能信3.1 什么是“TPS虚高”近几年大家讨论数据库性能时越来越爱提“TPS虚高”这个词。意思是压测工具跑出来的TPS很高但这个数字放到生产环境里没有意义甚至起误导作用。虚高不是工具造假而是测试模型和真实用户行为脱节产生的“失真”。最常见的虚高原因有三个压测脚本没有设置思考时间think time。真实用户在页面查询后至少会看一眼结果再操作而压测脚本如果循环之间没有间隔数据库就像在被“死命抽打”CPU跑得很高TPS也好看但生产环境根本不会有这么密集的请求。交易并发比例不真实。真实业务往往是80%的查询、15%的更新、5%的复杂报表但很多人压测时只跑一条主键更新语句TPS看起来很高却评估不了范围查询、排序、大事务带来的锁竞争和I/O压力。错误处理和超时被忽略。JMeter默认对超时或断言失败的事务也许也会记入数据统计口径一旦错了TPS自然虚高。通常只看成功事务数时如果响应时间已经飙升到客户不可接受这时的TPS再高也是“假的”。3.2 混合测试模拟真实业务的关键设计要避免虚高还是得回到JMeter里做混合场景。所谓混合测试就是在一个线程组里同时按比例运行多个不同的Sampler比如下单交易、订单查询、库存修改、日志写入。每个Sampler的权重按照真实业务的占比去分配。举个例子。假设一个电商库的业务构成大致是订单创建占20%订单查询占50%库存扣减占10%对账查询占20%。在JMeter里可以用Switch Controller或者Throughput Controller组合组织这些Sampler也可以直接建立多个线程组给每个线程组分配不同比例的线程数。更精细的做法是在脚本里通过JSR223 Sampler计算权重并动态选择交易类型。我建议的方案是先把每个交易单独做成一个测试片段Test Fragment然后用模块控制器引用再通过吞吐量控制器设置占比。这样后续调整比例特别方便不用改一堆Sampler。跑混合测试时注意压测机也容易成为瓶颈JMeter的聚合报告里如果出现网络延迟导致的错误要先确认客户端资源是否够用别把客户端的瓶颈错怪到数据库头上。混合测试的另一个目的是找出“比例放大”后才会暴露的问题。单一事务并发再高可能锁竞争很小但混合事务里写事务和读事务同时操作同一批数据行锁、间隙锁、日志争用都会冒出来。很多一体机在厂商宣传材料里的测试是单一模型你能跑到的数往往和宣传数字不一致这在正常范围内混合场景下的表现才是选型参考。3.3 判断测试结果合理性的几个标杆我自己判断一组压测数据可不可信一般会同时看四个指标第一CPU利用率能不能达到合理水平。如果数据库服务器的CPU利用率只有30%TPS却标称几万要么是客户端打不上去要么是脚本里大量时间耗在等待而不是计算这笔账要算清楚。第二响应时间的分布。重点关注TP99而不是平均值。如果TP99是平均值的5倍以上说明系统在某个临界点开始排队了这时的TPS要谨慎参考。第三资源瓶颈在哪个环节。跑混合测试时可以监控磁盘I/O、网络带宽、锁等待、redo日志写入量哪个先到瓶颈TPS的天花板就在哪。第四系统的稳定性。跑5分钟和跑1小时的结果可能是两回事内存泄漏、连接堆积、日志膨胀会在长稳测试里现出原形。用一句话总结TPS不是单点指标它只有在“资源利用率健康、响应时间可控、错误率接近零、长时间稳定”这四条同时成立时才有意义。4. 怎么给某个交易限制TPS从JMeter到数据库端的完整方案4.1 为什么会需要“限制某个交易TPS”这个需求初看有点反直觉——压测不是要把TPS拉得越高越好吗怎么会有人要限制TPS实际场景还真不少。最常见的是混合测试里的“按比例控速”。比如真实业务要求下单交易峰值不超过每秒200笔你压测时如果没有限速下单交易可能冲到每秒500笔把共享资源全吃掉查询交易就会大幅退化。为了让测试贴近现实容量就要主动给某个交易设置TPS上限。其次是生产数据库的保护。某些慢查询或批量任务如果无限制地并发执行可能会占满连接池把关键交易拖垮。通过限流让这个交易最多跑多少TPS就能把影响控制在可控范围内。再有就是SLA验证。客户要求某个接口的TPS不能低于多少同时也要求不能超过多少因为超过意味着上游系统可能扛不住这种情况下限流就是硬需求。4.2 JMeter限流Constant Throughput Timer还是Throughput Shaping TimerJMeter里最常见的限流组件有两个一个是自带的Constant Throughput Timer另一个是配合插件使用的Throughput Shaping Timer。Constant Throughput Timer的作用是让线程组以接近“每分钟X次”的速率发送请求。配置里的Target throughput是每分钟请求数Calculate throughput based on有几种计算方式常用的是All active threads in current thread group。它的实现方式是每个线程运行完一次后计算提前或滞后量再动态sleep补足时间差。优点是简单不需要装插件缺点是速率波动比较大尤其在线程并发高、单个事务耗时不均匀时实际TPS会来回震荡。Throughput Shaping Timer是JMeter Plugins Manager里的组件可以按时间段定义TPS曲线。比如前60秒从0慢慢升到200中间120秒保持200稳定后面60秒再降为0。它能更平滑地控制请求注入速率也更符合“阶梯压测”和“稳定性压测”场景。因为限流更稳我在做正式基准测试时一般首选它。如果只需要限制某个Sampler而不是整个线程组可以把对应Sampler放在独立线程组里再用Throughput Shaping Timer绑定到这个线程组。这样其他交易组照常压只有这个交易组被限制在指定TPS范围内。4.3 数据库端限流连接池配额、并发控制与队列但JMeter限流只解决了测试端问题。生产环境如果某个应用真的一股脑把请求打到数据库上光靠应用层自觉限制是不够的更稳妥的做法是在数据库访问链路里加一道闸。连接池是最容易入手的地方。假设某个交易独占一个专用的数据库账号或连接池那么把连接池的maximumPoolSize设小就直接限制了并发数进而限制了最大TPS。比如每个事务平均耗时50ms一个连接每秒最多约20个事务那么配置10个连接就意味着理论上限200TPS左右。按这个思路可以倒推连接数。另一种方式是使用数据库侧的并发控制或资源组特性。部分商业数据库支持资源组可以把某个SQL指纹或账号划分到低优先级资源组限制其并行度和CPU时间片。MySQL没有这么细粒度的资源隔离但可以通过中间件如ProxySQL做查询规则把特定SQL路由到独立的连接池或者干脆拒绝超出阈值的请求。开源的conntrack之类的机制也能用不过要小心对正常业务的影响。还有一个比较实用的是“令牌桶”思路。在应用侧用一个轻量的分布式限流组件每秒往桶里放固定数量的令牌请求打到数据库前先取令牌取不到就排队等待或快速失败。这样能精确控制单个交易的TPS但需要开发配合改成限流组件对老系统的改造成本会高一些。相比之下用连接池和中间件方式对应用侵入最小也最容易落地。4.4 回压测试与SLA验证的一个小技巧限制TPS不光是为了不让系统过载也常用来做“回压测试”。所谓回压就是当数据库处理不过来时请求方是排队等还是直接报错。在JMeter里结合Throughput Shaping Timer把TPS逐步提高观察数据库TPS上升曲线一旦发现数据库TPS增长开始明显放缓而响应时间和队列长度开始飙升那个拐点就是系统吞吐的真实上限。这种拐点测试比一股脑并发打满更能体现系统的软硬协同能力。我在实际项目里给客户验证一体机性能时通常会按“阶梯升温”的方式做TPS从100开始每5分钟涨100直到出现拐点。这样做出来的数据既有说服力也不会一上来就把系统压垮。限流组件在这里起了关键作用不然很难做到平稳的阶梯注入。5. 高性价比一体机的选型经验别只看配置单要验证软硬协同聊到这儿你大概理解了配置相同的机器为什么会跑出相差一倍的TPS。那回到采购选型这个实际问题上怎么判断一台“高性价比数据库一体机”是真好还是虚标我的建议是不管厂商的宣传页写得多漂亮拿到机器后一定按下面几个动作做一轮实测和探查。第一确认NUMA和绑核能力是否开放。很多一体机的软硬协同体现在固件和驱动层如果管理界面里能直接配置CPU亲和和中断绑定说明厂商在这块下了功夫如果什么都没有只能当作普通服务器自己折腾。第二用你自己的混合脚本去跑不要用厂商的演示测试包。厂商提供的压测脚本通常是他们最擅长的模型不一定匹配你的业务。哪怕先从生产环境抓取一个交易比例做成简化版混合场景也能看出差距。跑的时候记得监控CPU的user态和sys态比例。如果sys态占得过高说明内核和驱动层不够顺滑这种系统到高并发时大概率会拖后腿。第三验证“稳定后的TPS”而不是“瞬时峰值TPS”。缓存未预热时的短时飙高没有意义跑满1小时以上看TPS曲线是否平稳TP99是否可控。真正的高性价比不是峰值有多猛而是长时间负载下不衰减、不抖动。第四考虑运维和调优成本。软硬协同做得好的机器DBA上手以后需要调整的参数很少很多基线在交付时已经配置好出了问题也更容易定位。如果一台机器买回来便宜却要花两周时间去调内核和数据库参数那段时间成本和风险成本都应该算进总成本里。最后再分享一个个人习惯在做每台一体机的验收测试时我都会把上述涉及的协同相关配置项逐一截图存档包括numactl --hardware的输出、网卡队列绑定、大页配置、I/O调度器、数据库关键参数。等将来遇到性能瓶颈先对照这份基线看有没有配置漂移。很多时候“同样的机器突然慢了”并不是机器坏了而是某次系统补丁或运维操作把绑定关系重置了。这套方法帮我在不少项目里省下了排查时间建议你也在自己负责的环境里提前留下一份“配置快照”。回到开头的场景客户A的机器后来就是按照客户B的软硬协同配置重新做了绑定和调优TPS虽然没有完全追上但从21000提升到了35000以上。配置没变、数据库没换只动“软”的部分就挤出了60%以上的性能。这就是软硬协同的价值它不写在配置单上却真真切切地决定了每一核CPU能发挥出多少力量。