前言
随着工作年限增长,对服务的可用性和可扩展性会有更深的思考。从最初对 JDK API、NIO 到 JVM 的疑问,工作几年后,焦点自然转移到了服务如何平滑扩容上。这其实是个老生常谈的话题,但在高并发场景下依然值得深究。
正常情况下的服务演化之路
回顾一下后台架构的典型演进路径。
单体应用阶段,创业公司大多基于 SSM 或 SSH 起步,这是每个程序员的必经之路。
当业务规模扩大,我们需要水平扩容。只要保证服务无状态,增加实例即可。此时引入 RPC 框架(如 Dubbo)来管理服务间调用,减少直接连接数据库的压力。
如果业务继续高速发展,数据量激增导致 SQL 变慢,数据库成为瓶颈,分库分表便成了首选方案。无论是通过 ID Hash 还是 Range 方式,都能在一定程度上缓解单表压力。
但这真的能解决无限扩容吗?
实际上,上述架构存在一个隐形瓶颈:数据库连接数。
在微服务架构中,应用通常通过中间件(如 Sharding JDBC)访问数据库。这意味着应用并不感知具体要访问哪个物理库,规则由中间件决定。结果就是,一个 RPC 应用必须和所有分片后的数据库建立连接。
假设我们有 30 个 RPC 应用,每个应用连接池大小为 8,每个 MySQL 实例需要维护大量连接。MySQL 默认连接数有限,最大连接数也非无限。当应用数量达到一定阈值,数据库连接池耗尽,扩容就无法继续了。即便前面加一层 Proxy,Proxy 自身的连接数限制同样会成为新的瓶颈。
问题的核心在于'每个应用都要连所有的库'。应用扩容的同时,单个数据库的连接数也在线性增长。单纯增加数据库节点并不能解决连接数爆炸的问题。
单元化方案
为了解决'数据库连接数过多'导致的扩容上限,业界通常会采用单元化架构。
思路很简单:不让应用连接所有的数据库。
假设我们将数据按 Range 分成 10 个库,对应部署 10 个应用单元。让每个应用只连接一个对应的库。当应用增多时,如果连接不够用,就将逻辑库拆分为更多物理库,确保每个应用只负责一部分数据。这样,无论应用扩容到多少,都不会导致单个数据库连接数过载。
但这有个前提:请求路由必须精准。用户进入系统时,就必须知道该去哪个应用单元。通常需要在 DNS 层或网关层就定好规则,例如通过用户 ID Hash 计算归属单元,并由配置中心广播规则,确保所有组件认知一致。
通过这种单元化隔离,我们终于解决了无限扩容时的连接数瓶颈。
总结
从单体到微服务,再到分库分表,架构演进一直在寻找平衡点。分库分表虽然缓解了存储压力,但无法彻底解决连接数限制带来的扩容天花板。单元化架构通过绑定应用与数据单元,配合 DNS 或网关路由规则,确保每个实例仅访问对应数据库,从而突破连接数限制。该方案解决了扩容难题,但也引入了更高的复杂度和对可用性的挑战。
技术选型没有银弹,只有最适合当前阶段的方案。


