系统吞吐量的核心——队列,不仅仅是排队那么简单
排队论?利特尔法则?别急着点头。很多架构师对吞吐量的理解止步于“提高并发”、“增加机器”,天大的误解。今天我们把“吞吐量”剖开,看它的骨架——不是你想的那套流水线模型。真的,那太表面了。
假设你是一个快递分拣中心负责人。每小时处理包裹数——这就是吞吐。想提升?雇更多分拣员?扩大场地?但真正的瓶颈可能不在你想象的地方。实际上,包裹到达的间隔、分拣员操作耗时、传送带速度,它们交织成的概率分布才是魔鬼。我发现市面上80%的系统优化,都在瞎使劲。有意思吧?
计算机世界里,吞吐量通常用QPS(每秒请求数)或TPS(每秒事务数)衡量。但隐藏在背后的是并发度与延迟的对偶关系——没错,它们相爱相杀。举个例子,一个同步阻塞的Web服务器,每个请求占一条线程,线程池只有200个线程。平均响应时间100ms。理论吞吐量:200 / 0.1s = 2000 QPS。实际上呢?一旦请求到达速率超过这个值,线程池迅速饱和,后续请求排队,响应时间恶化,吞吐反而下降。诡异吧?这就是我强调的:吞吐不是个恒定值,它是系统在特定负载下的稳态输出。

为了看透这点,我早年做的一个IM网关的重构,至今印象深刻。旧版基于Tomcat BIO线程模型,线上QPS达到1200左右就撑死,CPU利用率不到30%,但线程数飙升到3000,操作系统频繁上下文切换,响应时间从50ms飙到2秒。看似还有CPU资源,但调度开销吞掉了所有增益。后来我们迁移到Netty的NIO模型,线程数固定为CPU核心数的两倍,调整了工作线程与I/O线程的配比。猜怎么着?同样的硬件,QPS冲到了18000,延迟依旧平稳。这不是魔法,是排队论里的M/M/c模型在起作用。服务节点c的个数与队列长度、等待时间可精确计算。优化的本质,就是找到系统中哪个“服务员”成了瓶颈,然后拆解它。
别被平均指标骗了。吞吐的崩坏往往是突发性的,就像高速公路在密度达到一个阈值后瞬间堵死。压测时,必须盯着长尾延迟(P99、P999)。一个系统的真性能够量,在于高负载下延迟分布的尾巴会不会突然变胖。当P999从10ms跳到500ms,吞吐量也基本到头了,再推流量就是找死。
实测对决:数据库连接池设多大才不拖吞吐后腿?
这个问题,各派的争论从未停过。最常见的一个陷阱:连接池越大,吞吐越高?错得离谱。我们团队做过一次完整的A/B对比,结果打了很多人的脸。测试目标:PostgreSQL 13,运行在64核128G的服务器上,用YCSB压测。固定业务:混合读写,90%读,10%写。
- 场景A:连接池大小=20
- 场景B:连接池大小=200
- 场景C:连接池大小=500
当并发客户端数只有50时,三者吞吐差不多,都在5000 TPS左右。当客户端数增加到400时,A的吞吐跌到3000,连接等待超时频发;B的吞吐维持在21000,且响应时间中位数稳定在28ms;C呢?吞吐16000,P99达到800ms。没想到吧,连接数过量导致数据库内部的锁竞争(特别是WAL和Buffer Pool的Latch)急剧增加,上下文切换飙升,磁盘IO也增多。后来我们通过操作系统级工具观察到,连接500时CPU的sys%高达40%,而连接200时sys%只有15%。
终极法则:数据库连接池大小 = (核心数 * 2) + 有效磁盘数?这公式早就过时。现在的正确姿势是:考虑并发事务的等待时间分布。如果事务平均耗时0.01秒,期望并发数200,则连接数至少200/(1 – 等待I/O的阻塞时间占比)。但阻塞时间占比怎么测?用pg_stat_activity视图分析等待事件类型,计算非活跃时间比例。我们的经验值:通常OLTP场景,设置为核心数的4倍左右开始微调,观察TPS和延迟的拐点,才是正道。

落地必知的三个泥潭,和爬出来的办法
说了半天理论,真正上线才知道处处是坑。以下三个,几乎每个团队都会经历——别问我怎么知道的。
泥潭一:无脑的全局锁或粗粒度同步。 系统里一个同步块挡住所有请求,吞吐直接降级为单线程。前年我们有个订单服务,因为一个全局的“生成序列号”函数加了synchronized,在促销流量下QPS从2000崩成300。排查时以为死锁,后来发现就是排队等锁。解决:将序列号生成改为无锁的Snowflake算法(注意时钟回拨),或者用Redis自增,但要忍受单点风险。更优雅的是,把共享状态分段,例如ConcurrentHashMap的段锁思想,竞争热点分到多个桶里去。实现后,吞吐恢复到8000,CPU均匀分布。
泥潭二:缓存雪崩击穿后的惊群效应。 缓存过期的一瞬间,所有请求砸向数据库,造成瞬时吞吐塌陷,然后连锁反应,整个集群都拖垮。我们的微服务因为一个热点数据过期,数据库连接瞬间耗尽,服务全部503。后来我强制团队必须遵循“永远不过期+异步刷新”模式:缓存key不设过期时间,而是用后台线程定期根据版本号刷新。不得不设过期时间时,引入分布式锁/互斥更新,只允许一个请求去查数据库重建缓存,其他请求等待或返回旧值。这样熬过了一个双十一后,我再没半夜被报警吵醒。
泥潭三:网络带宽的“隐形天花板”。 吞吐上去了,CPU、内存都悠哉,可响应时间就是涨。一看网络监控,带宽跑满了!这是个明显的物理层限制,但很多人压测时不加外部依赖,忽略内网流量。我们一个视频处理服务,从10K QPS优化到100K QPS后,突然发现千兆网卡成了瓶颈。单个请求响应体1MB,100K QPS就是800Gbps,当然不可能。后来改为零拷贝、压缩传输,甚至把大文件走共享存储旁路。教训:吞吐性能模型必须涵盖从网卡到磁盘的每一个物理环节,缺一不可。
这篇文章没有给你画大饼,也没给什么银弹。吞吐量优化是水磨功夫,靠的是对每个系统调用的成本和队列行为的深刻理解。下次有人跟你说“并发调大点就行”,你可以把这篇链接甩过去。