ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

数据库一体机性能调优:从NUMA绑核到混合压测与限流的实战解析

2026/9/12 9:58:33 拓冰建站 浏览量
数据库一体机性能调优:从NUMA绑核到混合压测与限流的实战解析 上个月去帮一个客户排查数据库一体机的性能问题折腾了整整一周。起因特别简单两台机器配置完全一样都是48核、512GB内存、全闪存储连型号批次都相同但压测得到的TPS一个稳定在2.1万另一个只有1.1万上下差了将近一倍。客户一开始坚持认为是硬件故障CPU、内存、磁盘、网卡全测了一遍厂商也来现场巡检结论都是“硬件没问题”。最后定位下来问题全出在软件层面——内核参数、CPU亲和性、NUMA策略、IO队列几项配置的差异叠加起来把一台好机器硬生生拖成了“半血”状态。这种事儿在数据库一体机的日常运维里其实非常典型。很多人以为“一体机”就是买了一个预调好的黑盒子插上电就能发挥出标称性能。但实际上一体机只是把硬件和基础软件打包了真正让你花出去的钱产生价值、让同样的CPU核数跑出应有TPS的是硬件之上那一层“软硬协同”的功夫。这篇文章就把这次排查过程中最关键的几个环节完整拆开讲讲为什么48核配置能差出1倍TPS以及用JMeter做混合测试时那些虚高的数据是怎么来的最后再说说怎么在数据库和接入层给某个具体交易限制TPS。1. 为什么同样48核配置TPS却差一倍1.1 48核不等于“48个平等核心”先看懂NUMA和超线程先别急着喷硬件绝大多数同配置性能差异问题第一步就应该看CPU的物理架构。现在的x86服务器只要上了双路基本就是NUMA架构。以常见的2路48核服务器为例通常每颗CPU是24核内存控制器分别挂在各自的CPU上于是服务器被分成两个NUMA Node每个Node拥有24核和一部分本地内存。关键点在于CPU访问本Node内存的速度和跨Node访问远端内存的速度差距不是一星半点。跨NUMA的内存访问延迟通常要比本地访问高30%到70%如果数据库进程的线程被调度到了Node 0但内存却分配在Node 1那么每一次内存读写都要绕道UPI总线频繁的跨Node访问会让整个数据库的等待时间直线上升。这个问题在高并发事务场景下会被无限放大因为每一笔事务都涉及大量内存操作跨Node访问的惩罚直接体现在TPS上。再有就是超线程。48核物理核开启超线程后系统里会看到96个逻辑核但逻辑核之间是共享执行单元的。数据库这种CPU密集型负载很多情况下超线程不仅不能带来收益还会因为缓存争用和调度器把线程切到另一个逻辑核上导致性能不升反降。所以在做软硬协同调优时我一向建议先把超线程的影响单独测一遍不要想当然认为逻辑核多就一定是好事。1.2 TPS虚高单交易压测掩盖了真实性能问题说到TPS差距就不得不提压测方法这个更大的坑。排查过程中我发现客户最初给出“2.1万 vs 1.1万”这个结论时用的压测方式其实非常粗糙——只压了单个交易而且压测数据全是热点数据锁竞争、日志刷盘、网络往返这些真实业务里躲不掉的因素基本都被绕开了。这就是最近圈子里总在说的“TPS虚高”单交易、全热数据、无思考时间、无限并发一起压出来的数据数字确实好看但它和真实业务之间差了十万八千里。真实业务是混合流量——查询、更新、插入、删除各种交易按一定比例同时打进来数据有冷有热连接有建立有释放事务有提交有回滚任何一环发生变化TPS都会出现明显波动。所以后来我给客户重新出压测方案时第一件事就是把“混合测试”提上日程。具体怎么设计JMeter脚本后面第4章会展开讲但这里想先给大家提个醒以后再看到某个厂商或者某个同行汇报数据库一体机性能先别急着信那个单交易TPS数字先问一句“是不是混合场景下跑出来的”。这四个字就是分水岭软硬协同做得好不好在混合测试里一眼就能看出来。2. 软硬协同第一板斧把CPU资源“钉”在数据库进程上2.1 内核层调优隔离CPU、关闭NUMA自动迁移搞清楚NUMA的原理之后下一步就是动手让数据库进程乖乖留在本Node。这部分的第一个关键操作用一句话概括让操作系统少干预让数据库进程有固定的“家”。我这次排查时第一步看的就是内核启动参数。默认情况下Linux内核的调度器为了保证所有CPU核负载均衡会时不时把进程从一个核迁移到另一个核迁移本身是有代价的尤其是在NUMA架构下线程一旦从Node 0迁移到Node 1它之前在那个Node上分配的内存全变成远端内存性能立刻崩给你看。所以对数据库这类延迟敏感型应用我一般建议在/etc/default/grub的GRUB_CMDLINE_LINUX里加上CPU隔离参数把大部分核心从内核调度器中“摘”出来专门留给数据库进程用。一个可供参考的启动参数示例如下GRUB_CMDLINE_LINUX... isolcpus2-23,26-47 nohz_full2-23,26-47 rcu_nocbs2-23,26-47 transparent_hugepagenever numa_balancingdisable这里简单解释一下几个参数的含义。isolcpus将指定CPU核从内核调度器中隔离出来普通进程默认不会调度到这些核上也就减少了内核线程和数据库线程抢CPU的情况。nohz_full关闭隔离核上的时钟中断减少不必要的周期性中断对数据库线程的打扰。rcu_nocbs把RCU回调从隔离核上挪走避免RCU机制带来的延迟抖动。transparent_hugepagenever关掉透明大页。数据库应用对内存分配模式很敏感透明大页容易造成内存碎片和分配延迟生产环境建议直接关闭。numa_balancingdisable关闭内核的自动NUMA平衡。自动NUMA平衡本身是好功能但对于数据库这种不吃这套的负载它反而会频繁迁移线程造成性能抖动。注意这里我特意保留了0、1、24、25几个核不隔离是为了让系统中断和内核线程有地方跑。如果你把全部核都隔离了网卡中断、存储中断没有CPU处理数据库进程反而会被反复打断那就得不偿失了。2.2 数据库层绑核numactl、taskset和SMP中断绑定内核参数调完之后还需要在数据库进程层面做绑定。这一步的核心工具是numactl和taskset。启动数据库时用numactl明确指定进程的CPU亲和性和内存分配策略numactl --cpunodebind0 --membind0 /path/to/database/startup这条命令的意思是数据库进程只允许在Node 0的CPU上运行内存也只从Node 0分配。这样做的好处是进程从启动那一刻起就已经把“家”安在了Node 0后续所有线程和内存分配都会优先留在本Node。如果是主从多实例部署还可以把不同实例分别绑到不同NUMA Node上比如实例1绑Node 0实例2绑Node 1两台实例互不干扰内存带宽和CPU资源都能充分利用。这一步对多实例混合部署的场景尤其有效曾经在一套48核机器上跑两个数据库实例绑Node之后整体TPS比之前涨了40%以上。中断绑定也是容易被忽略的一环。网卡、存储控制器都有自己的中断默认情况下这些中断很可能全部落在CPU 0上导致CPU 0被打满而数据库进程所在的核却在傻等。通过smp_affinity把中断分散到多个核上可以有效减轻CPU 0的压力echo 2 /proc/irq/中断号/smp_affinity这里的值是一个十六进制位图2表示只允许CPU 1处理该中断。实际生产环境中可以结合irqbalance或者自己写脚本把不同设备的中断分散到不同的物理核上。我个人的习惯是每个NUMA Node留一个核专门处理本Node对应设备的IO中断其他核全部留给数据库。3. 软硬协同第二板斧存储与网络IO路径不能拖后腿3.1 存储多队列、IO调度器与redo刷盘CPU层面的问题理顺之后第二板斧就是存储和网络IO路径。很多同配置机器在CPU利用率不高的情况下TPS却上不去问题往往出在IO路径上。现代NVMe SSD都是多队列设计配合内核的blk-mq机制每个CPU核都可以直接向设备提交IO请求减少了传统单队列的锁竞争。但前提是内核参数和队列深度要配置正确。我这次排查时发现客户机器上的IO调度器还是默认的deadline这在老式SATA盘上问题不大但在NVMe全闪盘上反而会增加无谓的排序和合并开销。对于数据库这种对延迟极其敏感的场景NVMe盘建议直接把IO调度器改成noneecho none /sys/block/nvme0n1/queue/scheduler改成none之后IO请求直接下发到设备层由SSD内部的调度逻辑处理延迟和吞吐都能得到改善。这个操作需要重启或者即时生效生产环境要小心操作。还有一个容易被忽略的点是数据库的redo/日志文件刷盘。事务提交时必须等待日志落盘成功这个fsync延迟直接决定了事务提交的极限速度。如果redo日志所在的存储卷IO队列深度配得太小或者被其他业务IO干扰那么TPS一定会被死死压住。建议把redo日志单独放在一个存储卷上并用fio工具提前测一下这个卷在最坏情况下的延迟表现确保p99延迟在个位数毫秒级别。3.2 网卡多队列、连接池与并发模型网络层面网卡多队列和RSSReceive Side Scaling是让数据库服务器扛住高并发连接的基础配置。默认情况下网卡中断会集中到一个CPU核上如果连接数上来这个核直接成为瓶颈。正确的做法是让网卡的队列数等于CPU物理核数同时开启RSS让每个核处理属于自己的网络中断。这里给一个配置RSS的参考方式不同网卡驱动命令稍有差异ethtool -L eth0 combined 48这条命令把网卡的combined队列数设置成48配合中断绑核可以让每个CPU核都参与网络包的接收和处理。对于数据库一体机来说这是让TPS稳定的基础条件之一。网络层还有一个重型话题数据库连接池到底开多大。很多人有个误解以为连接数越大TPS越高。实际上CPU核数固定时线程超过一定数量上下文切换的成本会急剧上升TPS反而下降。我在这次调优中专门做了连接数的梯度测试48核机器上数据库连接池从64个涨到128个TPS确实涨了但从128继续涨到256、512时TPS不仅没涨反而因为上下文切换和锁竞争开始下跌。连接池的大小怎么定没有一个固定的公式但可以按照“连接数≈CPU核数×(2~4)”这个经验范围做起步值再结合TPS和CPU利用率的拐点去微调。关键是你要通过压测数据找到那个“过了这个点再加连接数就没意义”的阈值而不是拍脑袋设一个看起来很大的数字。4. 用JMeter混合场景验收TPS别让数据骗了你4.1 从单交易到混合场景一份可复用的JMeter脚本设计CPU、NUMA、IO、网络这些软硬协同的底层工作做完了怎么验证效果直接用JMeter上混合测试。先说说为什么混合测试比单交易压测更能反映真实水平。真实业务里不同交易对资源的消耗差异非常大简单查询很快更新涉及锁大报表查询可能跑几秒写操作要刷redo。这些交易同时冲进来数据库内部会有锁等待、IO竞争、CPU排队。单交易压测把这些全过滤掉了你看到的只是数据库的理想状态。而混合测试就像把数据库扔进真实的人流里是骡子是马立刻见分晓。一份能拿来做上线依据的JMeter混合测试脚本至少应该包含以下几个要素。第一数据规模要贴近生产。不要用几百条数据做压测那等于让数据库在“缓存里玩”再差的配置都能跑出好数据。压测前导入生产级别或等比例缩放的业务数据确保不同数据分区冷热程度不同才能模拟真实IO行为。第二交易结构要按比例配。我常用的事务分布是查询类70%左右短更新类20%长事务或者报表类10%。JMeter里可以用随机控制器加权重因子来实现这种比例。第三并发模型要有思考时间。用户不会像机器一样毫秒不差地连续点击所以每个线程在事务之间需要加一个随机思考时间例如1到3秒。这个设计直接影响TPS的绝对值也更接近真实体验。第四事务内部尽量包含多条SQL。一个真实业务事务往往是先查询再更新再提交甚至包含多条SQL不是单条SQL跑完就算。一定要用事务控制器把多条SQL包起来并且显式提交commit不能靠自动提交糊弄。压测脚本的结构大致是这样的线程组 └─ 随机控制器按权重分配交易 ├─ 查询接口权重70 ├─ 更新接口权重20 └─ 报表接口权重10 └─ 固定/随机思考时间跑压测的时候建议先预热5到10分钟让数据库的buffer pool和执行计划稳定下来再正式采样15到30分钟。采样时间如果太短碰到一次垃圾回收或者checkpoint数据就会很难看很容易得出错误结论。4.2 如何判断TPS是“虚高”还是“实在”混合测试跑完之后接下来要做的就是擦亮眼睛判断手里的TPS到底值不值得信。这里画一条很简单的分界线单交易压测数据高不叫本事混合场景下TPS下降幅度可控才叫软硬协同到位。我一般从下面几个维度来判断检查项虚高表现软硬协同到位的表现单交易TPS vs 混合TPS混合场景掉一半以上下降幅度在20%~30%以内CPU利用率长期只有30%以下却自称高TPS核心线程CPU吃满整体可控响应时间平均值很低TP999突然飙高TP99和TP999平缓没有长尾尖刺数据库等待事件应用侧等得厉害数据库侧却很闲有合理的IO和锁等待没有单点堆积连接数变化加连接数TPS不涨反而乱跳连接数稳定TPS波动小这里多说一句看TPS不能只看总吞吐一定要看响应时间分布。尤其是TP999一个正常的系统不会允许大量请求的延迟是正常值的几十倍。软硬协同做得好系统在混合负载下的响应时间分布是平滑的做不好就会出现明显的长尾尖刺这种尖刺在单交易压测里根本暴露不出来。5. 怎么给某个交易限制TPS从数据库到接入层的完整方案5.1 数据库层限流资源管理器与资源组混合测试验证通过之后还有一个高频需求某个吃资源的交易把整个实例拖垮怎么单独把它限住比如每天上午9点的批量报表查询一个查询就可能消耗掉大量CPU和IO把在线交易全部堵死。这时候就需要给这个交易单独设置TPS或资源上限。先说数据库层的通用方案。很多数据库都提供了资源管理机制比如老牌的Oracle Database Resource Manager以及MySQL 8.0之后引入的Resource Group。它们的思路类似把特定用户、服务或会话归入一个资源组然后给这个资源组限定CPU使用率、并发度、IO优先级等指标。拿Oracle来举例可以通过DBMS_RESOURCE_MANAGER创建资源计划把负责报表的用户组放到一个CPU使用率上限比较低的组里同时限制并行度这样报表查询就算再慢再重也不会把在线交易的CPU份额抢光。MySQL的话可以用CREATE RESOURCE GROUP给特定线程组绑定CPU核并设置线程优先级再把需要限流的会话手动放进这个资源组。这类数据库层方案的优点是精准能控制到资源消耗的最底层。缺点也很明显配置相对复杂需要在测试环境反复验证。在调整前我强烈建议先用监控确认目标交易的平均资源消耗和峰值资源消耗再决定资源组的上限而不是随便拍一个百分比就能完事。5.2 接入层与应用层限流令牌桶与滑动窗口数据库层的资源管理解决的是“这个交易不能吃太多资源”的问题而接入层和应用层限流解决的是“这个交易一秒最多能进来多少次”的问题。两者配合才能真正做到让某个交易限制TPS。接入层常见的方案是用网关或负载均衡器做限流比如Nginx的limit_req模块、APISIX的limit-count插件、Spring Cloud Gateway的RequestRateLimiter过滤器。用得最多的算法是令牌桶和滑动窗口。令牌桶的核心理念是系统以固定速率往桶里放令牌每个请求进来先拿一个令牌桶里没令牌就拒绝或者排队。这样即使上游瞬间涌入大量请求最终落到数据库的TPS也是平滑可控的。给一个参考配置思路比如限制某个交易接口TPS不超过50Nginx的配置大致如下limit_req_zone $binary_remote_addr zonetrade_limit:10m rate50r/s; server { location /api/trade { limit_req zonetrade_limit burst20 nodelay; proxy_pass http://backend; } }这里的rate50r/s就是每秒50次请求的令牌桶速率burst20表示允许短时间内的突发20个请求。nodelay参数的意思是突发请求不用排队等待直接放行但后续超过速率的请求会被拒绝。应用层限流则更灵活像Sentinel、Resilience4j、Guava RateLimiter这类库可以直接嵌在业务代码里对某个具体的service方法做限流。相比接入层的IP维度应用层可以做到按用户、按账号、按订单类型做更细粒度的控制。比如我之前在某个项目里就针对大客户的批量同步接口单独加了令牌桶限制这个接口的TPS不超过20既保住了大客户的需求又不影响普通用户的在线交易。5.3 落地示例给一个“吃CPU大户”交易设定限流阈值最后分享一个近期项目的落地示例方便大家把前面这些串起来。业务背景是一套48核数据库一体机上跑着一个核心交易系统每天上午10点左右一批报表查询任务会定时触发这些查询单条要跑5到10秒并发上来后直接让CPU飙到95%以上导致在线支付的TPS从平时的3000掉到不足800。客户的需求很明确让报表查询不要再拖垮在线交易同时允许报表每天跑完。我们做的配置分三层。第一层是接入层。在网关里为报表查询接口单独配置了一个限流规则rate限制为20r/sburst为5。这样无论调度平台怎么并发触发真正能打到数据库的报表请求每秒最多只有20个左右在线交易永远能占据绝大多数CPU资源。第二层是数据库层。通过数据库资源管理器把报表查询所用的数据库账号归入一个CPU使用率上限为30%的资源组同时限制该组的并行度不超过4。这样做的好处是即使报表请求进来了它最多也只能吃掉30%的CPU份额剩下的70%留给在线交易万一报表请求被限流堆压也不会影响核心交易。第三层是应用层。在报表服务的代码里设置了语句级超时时间单条大查询超过30秒直接中止并在应用层做了排队机制超过队列长度的请求直接快速失败并重试而不是无限制地堆积在数据库连接池里。这套方案上线之后报表查询高峰时段在线交易TPS稳定在2800到3200之间CPU利用率被压在一个相对平缓的曲线附近。效果立竿见影后台报表也能在宽松的时限内跑完两边都保住了。6. 这次调优之后我留存下来的几条实战体会这次调优的最后一环是给整个方案做个阶段复盘。我最大的体会是软硬协同不是玄学它是一整套可以被验证、被量化、被复现的操作方法。CPU隔离、NUMA绑核、IO多队列、混合压测、限流策略每一项单独拿出来都是老生常谈但能把它们按顺序组合起来让一台48核机器在混合场景下跑出真实的高TPS这才是数据库一体机真正的分水岭。复盘时我还发现一个规律很多团队纠结于硬件选型却忽略了软件配置的精细度。换一台更贵的机器远不如把现有机器的内核参数、CPU亲和性、IO路径和压测方法理顺来得实在。我见过太多“48核机器跑出24核效果”的案例最后查下来全是这类软配置问题。如果你现在也在为数据库一体机的性能头疼我的建议是从基线开始先跑一遍混合场景压测记录下TPS、CPU利用率、响应时间分布和数据库等待事件然后一次只改一个配置项记录变化。不要试图一天把所有参数都调到位那样出了问题你都不知道是哪个调整带来的副作用。最后分享一个操作上的小心得做限流的时候阈值永远不要拍脑袋定。从生产监控里取目标交易过去一周的P99和P999指标再乘上1.2到1.3的余量系数这才是合理的限流数值。限流限得太紧业务受损限得太松又起不到保护作用。数据和监控才是软硬协同这条路上最值得信任的伙伴。