大厂的光环、先进的技术栈、租户间看似完美的资源隔离、一张张无可挑剔的性能指标——在所有相关方眼里,答案早就写好了:问题一定出在应用系统。
可我这个当年婉拒大厂 offer、在医疗信息化里泡了十七年的技术人,每一次质疑都被一句轻飘飘的“不可能”略过。十七年摸爬滚打的直觉,敌不过一个看似权威的判断。
质疑没人回应,剩下的路只有一条:用实测自证。
做数据库和中间件的同学,大概都经历过这样的时刻:业务偶尔变慢、报表跑不动、大查询卡住,第一反应往往是——是不是被限流了?
这个怀疑合情合理,但也很容易变成甩锅。OBProxy(ODP)作为 OceanBase 的访问代理,天生带着流量控制、连接数限制、内存保护这些机制,怎么看都像会限流的样子。可它到底限没限?限的是什么?限流是不是真的在压制你的业务吞吐?
为了回答这个问题,我以 OBProxy 4.3.1 为核心,用自研压测工具跑了两套环境、多个维度的系统验证,累计约 1700 万次请求,零失败。这篇文章不堆术语,只把四个最关键的问题讲清楚:限流机制到底开没开、字节降速算不算限流、真正的天花板在哪、以及你很可能忽略的一笔隐性税费。
01 限流机制开了吗?开了,而且是真的在干活
先说结论:OBProxy 4.3.1 的限流机制默认就是开启的,而且不是配置摆设——我通过配置层加行为层双重验证确认了这一点。
配置层看得很清楚:隧道水位流控 enable_flow_control=True、单连接缓冲高水位 64K、连接数上限 8192、单会话内存上限 8MB,全部是出厂默认值。
官方文档对 enable_flow_control 的描述是:用于控制 MySQL tunnel 中是否开启流量控制。但它在 Oracle 兼容模式下到底有没有实际生效?这还得看行为层证据。我做了一组剂量-效应实验,把流控水位从 64K 调大到 2M,再彻底关闭。结果未饱和并发下的吞吐,从 1093 一路爬到 1271、再到 1350 QPS——流控限制越放松,吞吐越高,单调上升。这说明机制真实在环,不是在睡大觉。
02 字节降速也是一种限流——只是它不拒绝、不报错
这是整个测试最关键的结论,也最容易被误解:OBProxy 的字节降速(flow control 背压),本身就是一种限流机制。它按单连接的在途字节量来限——缓冲积压超过高水位就暂停读取、排空到低水位再恢复,把每连接的转发速度压在一个有界范围内。所以我们说的“流控在干活”,本质就是“字节降速限流在干活”。
但要注意,这种限流和我们常说的“拒绝型限流”不是一回事。整个测试累计约 1700 万次请求,零失败——没有错误返回、没有 QPS 封顶、没有周期性的压制锯齿。也就是说,字节降速是“降速等待”而非“拒绝报错”:它不会把请求打回去,而是让你排队、让你等。小查询在测试库跑到了 7.4 万 QPS、正式库 12.7 万 QPS,都是近似线性扩展、没到顶;压测里最高开了 512 个连接,全部秒建,而连接上限是 8192,远远没碰到。
这里要特别强调一个容易被忽视的点:字节降速这种限流的代价,直接落在延迟上——并发上来时,请求不是失败,而是变慢。测试里我们特意没给压测客户端设超时,所以是“零失败”;真实业务一旦设了超时阈值,这部分背压延迟就会悄悄转成超时失败。这一点下面会专门讲。
03 另一块天花板是物理墙
字节降速限流的原因除了大结果集返回外,会不会还有吞吐天花板?答案是一层层“物理墙”——它们也会间接导致字节降速限流。
我把同样的负载放到不同位置去压,结果非常直观:在办公网压,大结果集吞吐钉在约 117MB/s——这是客户端和机房之间的千兆链路极限;把同一负载搬到机房内压,吞吐立刻翻了 11 倍,到约 1.4GB/s。这反向证明办公网的瓶颈在链路,不在 OBProxy。
机房内这约 1.4GB/s 又是哪来的?我用了四步排除法:先排除租户配额(CPU 全程只用约 10%),再排除服务端 CPU 和带宽(OBProxy 转发 1.3GB/s 只吃了 0.35 个核、网络出口只用了约一半),再排除 JVM 进程墙(同一台机器开两个独立进程同时压,聚合吞吐并不翻倍),最后排除 OBProxy 单实例软件墙(两个独立实例分流,聚合吞吐依然不翻倍)。
四步排除后,结论收敛到压测客户端这一侧:由于只有一台虚拟机可以用来部署压测程序,实在没法再压测 OBProxy 的吞吐上限,只能怀疑 Linux 内核收包路径、网卡队列和软中断核分布 是最可能的天花板来源,有条件和兴趣的同学可以去压测一下。也就是说,测试里看到的约 1.4GB/s,其实是压测机自己收不过来了,而不是 OBProxy 发不出来——OBProxy 单实例的真实能力,在整个测试里根本没被压到顶。
正式环境也印证了这一点:经过 F5 的路径封顶在约 1.05GB/s,而我们绕过 F5 直连 OBProxy,同样负载能到 1.24 到 1.39GB/s——两者差了 11% 到 23%。这 11% 到 23%,就是 F5 这一层收的“税”。1.05GB/s 已经压到了 F5 的吞吐上限。有同学可能会说 F5 的最低吞吐怎么也得有 10G 吧?注意 F5 的 10G 吞吐是 10Gb/s,B 和 b 差了八倍关系,同样跟 OBProxy 无关。
04 最容易被忽略的:默认配置在悄悄收“吞吐税”
上面说字节降速限流不拒绝请求,但有一个地方必须承认:这种限流确实在收钱,只是收的是“吞吐税”,而且默认配置收得不少。
回到那组剂量-效应实验:默认 64K 水位下,未饱和并发的吞吐比彻底关掉流控低了约 19%;把水位调到 2M,这个税就缩到只剩约 6%。换句话说,默认的 64K 水位,在业务还没饱和的时候就先扣了你将近两成的吞吐——而这笔钱买来的保护价值在当时几乎为零(并发只有 100,缓冲才 6.4MB,根本不需要控)。
把高水位调到 2M,是这套测试里验证出的最优甜点:低并发时只比关闭低 6%,高并发时反而反超关闭 10%。而且它有内存收益——2M 水位在高并发下缓冲有界,不会失控。
这里必须给一个反向警告:别图省事直接关闭流控。实测关闭后,高并发下吞吐反而塌陷了 8% 到 9%,延迟也最高;更危险的是,每连接的在途缓冲会变成无界,512 并发理论上能堆到 4GB,如果进程内存限制过低会直接撞破进程内存上限、导致进程退出。关闭流控,等于在高并发时同时背上“性能退化”和“内存耗尽”两重风险。但是高水位值到底应该设置多少,需要根据自己的业务场景来评估。
05 给同行的一套判断方法
跑完这一轮,我们把“业务慢是不是被限流了”这个问题,沉淀成一套可复用的判断方法,分享给大家。
第一,先看清限流类型。OBProxy 的字节降速流控,本质是一种限流——它限制的是每连接的在途字节,表现是降速等待(排队、不报错),而不是拒绝报错;连接数上限是拒绝型,但阈值很高,普通业务根本碰不到;全局 QPS 限流在 4.3.1 版本里不存在。搞清楚“字节降速就是限流、只是不拒绝”这件事,比纠结参数值更重要。
第二,遇到吞吐封顶,先做位置对照。同一负载在本地和机房分别压一次,如果吞吐差很多,瓶颈在链路;如果两边都封顶在同一水平,再往下追服务端和客户端。
第三,用 payload 缩放判别 QPS 限流还是字节瓶颈。把响应体缩小三倍,如果 QPS 等比放大、字节吞吐不变,那是字节驱动(CPU/网络栈),不是 QPS 限流。
第四,做差分对比找“谁在收税”。正式环境里,绕过 F5 直连和走 F5 各压一轮,差值就是 F5 的税。这套逻辑也适用于其他负载均衡器。
第五,也是最容易被忽视的:检查业务超时预算。OBProxy 背压的代价是延迟,延迟超过业务超时阈值就会变失败。如果应用部署在办公网内部(不要不信,真有这么干的同学),大结果集场景下,512 并发时平均延迟 4.6 秒、P90 到 6.5 秒——如果你的业务超时只设了 3 秒,大规模取数时几乎必然失败。别急着怪限流,先看超时和链路。
掌控限流
把这一轮 1700 万次请求的测试讲完,最想传递的判断只有一句:很多“业务被限流”的直觉,其实只对了一半。
对的一半是:OBProxy 确实在限流——它的字节降速(背压)机制一直开着、一直在干活,只是它不拒绝你,而是让你慢下来,悄悄地收延迟的税;错的一半是:还有一层卡住吞吐的,是一层层物理墙——千兆链路、F5、压测机的收包能力,这个也会间接引起字节降速限流。
真正值得你动手的,是两件事:一是把流控水位从默认的 64K 调到 2M(不同行业可能不同),把那个近 20% 的隐性吞吐税收回来;二是把业务超时预算建立在真实延迟包上,别让背压延迟悄悄变成线上失败。
测试数据来自 OceanBase 测试库与正式环境的真实压测(OBProxy 4.3.1),结论覆盖小查询、大结果集、连接数等多个维度。本文由 云栈社区 整理发布。