说实话,我第一次认真研究“分布式数据库代理”这个话题,是因为一次非常狼狈的容量规划。当时团队把订单库从单机拆成了十几个分片,每个分片还挂了主从,以为上线后就万事大吉了。结果压测一开始,后端数据库实例的连接数直接被打满,应用层报错刷屏,一堆服务开始超时。我们排查了很久才发现,问题根本不在于数据库本身,而在于所有应用都直接去连那一堆分片和副本,连接数是乘法关系往上翻,谁也扛不住。后来引入了一层数据库代理,才算是把访问入口收住,整个链路才重新变得可控。
这篇内容就是围绕“分布式数据库代理”来写的:它到底是什么,解决了哪些核心问题,选型时该怎么权衡,以及我在实际搭建和排障过程中的经验。不管你是做后端开发、DBA还是架构设计,只要你的数据库开始走向分布式、分片化,这篇文章应该能帮你少踩几个坑。
1. 为什么需要数据库代理——先从一次真实的容量规划说起
1.1 节点变多之后,应用已经不知道怎么连数据库了
很多人刚接触分布式数据库时,关注点都在数据分片、分布式事务、一致性这些方向上,但真正落地时第一个被击穿的往往是连接管理。
举个很典型的场景。你的订单库从单机1主1从,扩成了16个分片,每个分片一主两从,那后端就是48个MySQL实例。如果应用直接连接这些实例,连接数怎么算?假设你有40个应用实例,每个实例连接池配置20个连接,那后端理论上要承受 40 × 20 × 48 的极端连接压力。就算实际达不到这个峰值,日常的存量连接也足够让数据库的线程调度变得异常吃力。
我当时遇到的情况还要更隐蔽一些:连接数没有立刻爆掉,而是每隔一段时间就会有一波新建连接风暴。原因很简单,应用直连多个后端节点时,只要某个节点抖动一下,连接池重建,就会瞬间产生大量TCP握手和MySQL握手。数据库节点本身CPU不高,但连接线程一多,整体RT就开始恶化,进而引发更多超时和重连,形成恶性循环。
这就是分布式数据库带来的第一个“幸福的烦恼”:节点多了,拓扑复杂了,应用已经不知道怎么去连数据库了。你不可能要求每个业务开发都记住分片规则、知道哪个库是主库、哪个从库可以读,更不可能让他们在代码里自己实现故障摘除和Failover逻辑。
1.2 代理层解决的三个核心问题
要解决上述问题,数据库代理其实只干三件事:连接收敛、拓扑屏蔽、策略统一。
连接收敛很好理解。应用无论有多少实例,都只认一个逻辑地址,就是代理的地址。代理负责与后端所有数据库节点保持长连接池,应用发过来的连接请求只是在代理上报个到,实际后端连接是复用的一批存量连接。这样后端连接数不再是“应用实例数 × 数据库节点数”,而是“代理节点数 + 数据库节点数”,数量级一下子就下来了。
拓扑屏蔽的意义更大。分片规则、主从切换、节点扩缩容,这些细节全部收进代理内部。对应用层来说,它看到的永远是一台或者几台逻辑数据库。你后端换了一个分片节点,应用连的DSN都不用变,代理把它屏蔽掉了。这对研发团队来说简直太重要了,因为数据库拓扑变了,往往不需要业务发版本。
策略统一则是治理层面的价值。SQL限流、审计、脱敏、读写分离路由、慢SQL拦截,这些能力如果分散在每个客户端的连接池配置里,根本没法统一管理。放到代理层,就是一处配置、全局生效。
1.3 不要把代理理解成“让数据库更快”
有一句大实话得说在前面:数据库代理不会让单个SQL执行得更快,它甚至还会引入额外的网络跳转和SQL解析开销。那为什么还要用?
我习惯把代理比作公司前台。前台不会帮你更快写完代码,但她能帮你拦下无关访客、把快递分类、告诉你哪个会议室在哪。如果没有前台,所有人都直接往工位里闯,每个员工都得自己处理这些杂事,公司规模一大必然乱套。
代理也是这个定位。它是架构意义上的“访问治理入口”,而不是性能加速层。只有在两层情况下你感觉“变快了”:一种是从库读分担了主库压力,整体吞吐上去了;另一种是代理把那些连错节点、重复建连的浪费消掉了,系统稳定了,自然不卡了。本质上它优化的是系统的吞吐和稳定性,不是单条SQL的执行效率。
另外要区分清楚:TIDB这样的原生分布式数据库本身就自带接入层,它对外提供的那个“数据库入口”其实已经是代理能力的一部分;而ShardingSphere-Proxy这类中间件代理,则是把代理能力做成一个独立服务,放在应用和底层MySQL/PostgreSQL之间。两者定位相似,但前者和存储引擎强绑定,后者更通用,可以接多种数据源。理解这一点,后面选型的时候才不容易混乱。
2. 核心功能拆解:代理层到底在干什么活
2.1 连接收敛:如何把“万人连接”降级成“千人连接”
连接收敛听起来简单,实际做起来有不少细节。
首先代理与后端之间,必须是“长连接复用”。也就是说,无论有多少客户端连接经过代理访问某个分片,代理到该分片只维持固定数量的真实连接,通常做一个连接池来管理。客户端连接即使频繁创建销毁,也不会直接传导到后端。
其次是代理与前端的连接。很多代理产品支持连接池模式,也支持直连模式。连接池模式的好处是能快速响应应用请求,但代价是代理自身需要维护更多socket和内存;直连模式则更省资源,但每次新建连接都会有一次完整的握手开销。我个人的经验是:在高并发场景下,代理最好配置一个合理的“前端连接池上限”,避免瞬时连接数冲得太高,后端没挂、代理先顶不住了。
这里还有一个很多人会踩的坑:代理的连接数上限需要和后端实例数量、后端thread pool大小一起评估。如果代理允许的并发连接数设得比后端max_connections还大,那连接风暴只是从后端转移到了代理,并没有真正被消灭。压测阶段就要把整条链路的连接水位测一遍。
2.2 SQL路由:从SQL解析到目标节点映射
路由是分布式数据库代理最核心的能力之一,也是大家选择它作为分库分表入口的主要原因。
一条SQL到了代理手里,大体要经过三个步骤:解析SQL,提取分片键,映射目标节点。
举个例子,订单表按order_id哈希分成两个分片。应用执行一条查询:
select * from t_order where order_id = 10001;代理拿到这条SQL之后,会解析出等值条件order_id = 10001,然后对这个值做哈希取模,根据分片算法算出来它应该落在哪个分片节点上,最后只把SQL转发给那一个节点执行。这个过程的预期是让查询精准命中目标分片,减少跨节点访问。
但问题往往出在“没有分片键”的SQL上。典型的场景是:
select * from t_order where user_id = 12345;如果分片键用的是order_id,那代理没法直接算出user_id对应的分片,只能把这条SQL广播到所有分片节点,再把结果合并。一次查询变成多次后端查询,性能自然好不了。这其实不是代理的问题,而是分片键设计时留下的债务。
另外PreparedStatement(预编译语句)也是代理实现里一个非常隐蔽的深坑。文本SQL好解析,但预编译协议要先处理COM_STMT_PREPARE、再处理COM_STMT_EXECUTE,代理必须维护一套“语句句柄到具体SQL”的映射关系,同时还要跟踪参数类型。一旦某个客户端没有正确关闭语句句柄,代理内存里就会积压一堆PreparedStatement缓存,时间长了内存和CPU都会异常。这个在线上环境里特别常见,查的时候不妨先看代理的statement cache命中率和对象数量。
2.3 读写分离,不是简单“读去从库”
读写分离的初级理解是:写语句走主库,读语句走从库。但实际落地时,最大的问题是“这条读能不能走从库”,而不是“它是SELECT”。
事务中的读不能走从库。比如你在一个事务里先插入订单,再查询订单状态,如果查询走了从库,而从库复制延迟没追上,你很可能查不到刚插入的数据,业务逻辑就直接错了。所以代理的实现里,一旦检测到当前会话处于事务状态,事务内所有读都必须被强制路由到主库。
带锁的读也不能走从库。select ... for update这种语句,本质上是写语义,必须主库执行,否则锁在哪边都说不清楚。
还有一点容易忽略的:写入后的立即读。即使没有显式事务,业务上“写完马上要看到结果”的场景也很常见。比如支付回调后立刻查询订单状态。这种语句如果走了延迟较大的从库,就会出现“数据怎么又没了”的诡异现象。这种问题特别难排查,因为从库延迟通常只有几百毫秒,闪现一下就被覆盖了,但被用户秒点碰到一次就是P0事故。
所以成熟的代理方案一般会提供两种控制手段:一是事务内路由,自动保持会话一致性;二是通过SQL Hint或者配置强制指定某些读走主库。我在配置线上规则时,一直都是“能走从库的才走从库,不确定的一个都不放走”。
2.4 故障发现与自动切换机制
分布式数据库池子里的节点总有挂掉的时候,代理作为“唯一入口”,必须比应用更早感知到故障,并做出处理。
故障发现依赖探活。轻量一点的探活方式是TCP探测,也就是代理周期性检查后端端口是否可达;靠谱一点的探活方式是发送MySQL的ping命令或者执行select 1。TCP探测最大的问题是“端口还开着,但数据库已经假死”的情况探测不到,所以我更倾向于在MySQL协议层做探活。探活频率要克制,比如每1秒一次足矣,太频繁反而会给后端制造无谓的压力。
处理故障时,代理要干两件事:摘除故障节点,并把新的请求路由到健康节点;同时,已经建立的连接如果落在故障节点上,要及时断开,让应用层感知并重连。这里有经验的团队会把代理和数据库高可用组件配合起来,比如MHA、Orchestrator这类工具负责把从库提升为新主,然后代理感知到拓扑变化后把流量切到新主上。整个过程需要做一些移交预演,毕竟在线切库这种事容不得临场发挥。
我自己曾遇到过一种情况:后端主库被kill之后,代理的游戏规则是“探活失败摘除节点”,但那个节点上的已有连接没有及时断开,导致应用层一直往同一个socket上写数据,然后就是各种MySQL server has gone away。后来我们把代理的“失效连接回收时间”调短,配合应用连接池的重试机制,切主时间才压缩到业务可接受范围。
3. 方案选型与架构设计的几个关键权衡
3.1 集中式部署与客户端接入到底怎么选
数据库代理落到部署形态上,主要分成两类:集中式代理和客户端嵌入式代理(也有叫Sidecar模式的)。
集中式代理的优势是统一、可控、易治理。所有流量都经过一个明确的服务,监控、限流、审计都有了落点,后端拓扑变化也能隐藏在一个逻辑接入点后面。缺点是多了一跳网络开销,并且引入了中心组件的高可用问题。ProxySQL、ShardingSphere-Proxy就是这种路线的代表。
客户端嵌入式代理则直接“长”在应用进程里,通过类库的形式提供分片路由和读写分离能力。它的优势是延迟最低,没有独立代理的网络跳转;劣势是每一个语言都得维护一套SDK,控制面分散在各处,多个团队使用时规范很难强制统一。比如有的团队在代码里写了自定义路由,另一个团队用了另一套配置,最后DBA根本没法全局管理。
我的建议是:团队规模小、系统单一、追求极致性能,可以考虑嵌入式;一旦涉及多个业务团队、数据库种类也多,集中式代理会省心很多。架构的演进通常会从嵌入式开始,等规模大了再沉淀成集中式。
3.2 常用方案对比与选型建议
市面上的数据库代理方案,我大致用过几个,主观感受放在下面:
| 方案 | 形态 | 核心能力 | 适合场景 | 需要注意的点 |
|---|---|---|---|---|
| ProxySQL | 集中式代理 | 连接池、读写分离、查询路由、限流 | 纯MySQL场景,快速解决连接和读写分离 | 分片能力弱,更多是接入治理 |
| ShardingSphere-Proxy | 集中式代理 | 分片、读写分离、数据脱敏、分布式事务 | 分库分表场景,需要强路由能力 | 对复杂SQL兼容性需要充分测试 |
| Vitess | 集群化代理 | 大规模MySQL分片、在线DDL、备份恢复 | 超大规模、需要横向扩容 | 运维复杂度高,存储引擎绑定Vitess体系 |
| 原生分布式数据库接入层 | 内建代理 | 与存储引擎天然协同,分片、事务、高可用一体 | 直接选用新架构的团队 | 跟具体数据库绑定,迁移成本高 |
选型不要只看功能矩阵,更重要的是看“协议兼容能力”和“团队运维能力”。协议兼容能力的意思是,代理能无缝兼容你客户端发出的每一条SQL和每一种协议行为。很多方案在文档里写得很满,实际跑一条带子查询的多表Join就出了岔子。所以无论选哪个,上线前都要用真实业务的SQL流量回放做一轮兼容性测试。
3.3 代理层本身的高可用设计
引入了一个新中间件,它自己挂了怎么办?这个问题是必须回答的。
核心思路是让代理尽量“无状态化”。代理的状态主要存在于会话上下文、连接池、PreparedStatement缓存里。如果这些状态全部放在单机,那么这台机器一挂,所有连接都要重新建,后端同时涌来大量重连,非常危险。因此生产环境至少要部署两个代理节点,用VIP或者负载均衡器对外提供一个统一入口,后端挂了可以从另一个节点接上。
如果用的是负载均衡器,注意会话保持的连接如果在Failover之后还要复用,是做不到的,因为新节点的会话上下文不一定完整。所以应用连接池本身必须有足够的断线重连能力。换句话说,引入代理不能只部署代理,应用端的连接池参数也要针对“代理切换”场景做一轮适配:重试次数、重试间隔、maxTotal,最好都调到合理范围。
我见过一些团队为了追求高可用,引入三台代理加一套Keepalived配置,但从来没有做过主备切换演习。等到真挂了一台才发现,VIP漂移倒是成功了,但因为后端长连接全部失效,业务瞬间报了上千条错误。记住:高可用不是配置出来的,是演练出来的。
3.4 性能损耗的现实数据
代理的性能损耗,我在多个版本里做过对比测试,也比较过不同方案之间的差异。直接说结论:对于文本协议转发为主的场景,损耗通常在5%到10%之间,主要体现在小查询的P99延迟上;如果代理做了SQL重写、结果集合并、排序聚合这类工作,损耗会上升到15%,复杂分页场景下甚至更高。
压测方法很简单:先让应用直连数据库,跑一轮压力测试,记录QPS和P99;然后把连接地址改成代理地址,同样的参数再跑一轮,对比两组数据。
我在一个16核32G的代理节点上做过分库分表压测,直连MySQL的QPS大概在两万出头,走代理之后掉到一万八左右,P99从10ms涨到13ms。这个损耗我认为完全值得,因为你换来的是后端连接数大降、拓扑可以随时切换、治理策略可以统一管控。真正需要警惕的不是这5%的损耗,而是代理SQL解析逻辑写得糟糕,导致CPU直接被拖满。后面在排查篇里详细说。
4. 实操:搭一套分布式数据库代理并把核心场景跑通
4.1 测试环境拓扑规划
纸上谈兵再多,不如动手跑一遍。下面是我在测试环境里验证分布式数据库代理核心能力的一套标准拓扑,简单但覆盖面足够。
我准备了两台MySQL节点,一个节点模拟主库(source),一个节点模拟从库(replica),另外一台机器部署ShardingSphere-Proxy。Docker环境下可以用下面的方式快速起两个MySQL实例:
docker run -d --name mysql-source \ -p 13306:3306 \ -e MYSQL_ROOT_PASSWORD=root \ mysql:8.0 docker run -d --name mysql-replica \ -p 13307:3306 \ -e MYSQL_ROOT_PASSWORD=root \ mysql:8.0这不是完整的MySQL主从复制配置,只是先把两个实例跑起来。实际做主从复制时还需要分别设置server-id、开启binlog、配置复制账号,并执行CHANGE MASTER TO。这些步骤比较常规,就不贴完整命令了,但要注意主从复制的点位信息在切换和故障恢复时会非常关键。
代理节点我是直接用官方镜像跑的,数据源配置指向上面两个MySQL实例。先不要急着加复杂的规则,第一步验证“代理能连通、SQL能透传”,再做分片路由。
4.2 配置分片路由与读写分离
以ShardingSphere-Proxy 5.x为例,核心配置在一个YAML文件里。下面的内容是一个简化的分片加读写分离配置,只是演示结构,实际使用务必以对应版本的官方文档为准:
dataSources: ds_0: url: jdbc:mysql://127.0.0.1:13306/demo?useSSL=false username: root password: root connectionTimeoutMilliseconds: 3000 ds_0_slave0: url: jdbc:mysql://127.0.0.1:13307/demo?useSSL=false username: root password: root connectionTimeoutMilliseconds: 3000 rules: - !READWRITE_SPLITTING dataSources: readwrite_ds: writeDataSourceName: ds_0 readDataSourceNames: - ds_0_slave0 loadBalancerName: round_robin - !SHARDING tables: t_order: actualDataNodes: readwrite_ds.t_order_${0..1} tableStrategy: standard: shardingColumn: order_id shardingAlgorithmName: order_hash_mod shardingAlgorithms: order_hash_mod: type: HASH_MOD props: sharding-count: 2配置好之后,用MySQL客户端连代理,创建两张分表,再写几条数据验证:
create table t_order_0 (id bigint primary key, order_id bigint, user_id bigint); create table t_order_1 (id bigint primary key, order_id bigint, user_id bigint); insert into t_order (id, order_id, user_id) values (1, 101, 1001); insert into t_order (id, order_id, user_id) values (2, 102, 1002); insert into t_order (id, order_id, user_id) values (3, 103, 1003);然后分别查询order_id=101和order_id=102,再到后端两个真实MySQL实例上看数据落到了哪张分表。只要路由规则生效,你会发现两条记录分别进了不同的物理表。再到代理上执行一条select * from t_order where user_id = 1001,它会触发广播查询,从代理日志和后端实例执行记录里也能看出区别。
这套实验做完,你对“分片路由”和“广播查询”的感知会比看十篇文章都深。
4.3 故障切换实测
路由验证通过后,再做故障切换。这个实验强烈建议你亲自做一次,因为理论和实际差别太大。
操作很简单:在主库容器里执行:
docker stop mysql-source然后观察代理日志。正常情况下,代理会在下一个探活周期发现主库不可达,标记节点失效,并把后续写流量路由到从库。但这里有一个大前提:从库必须已经被提升为新主,否则代理只是把流量切过去,从库本身没有写能力,业务写入还是失败。
我们当时是把切换能力交给外部高可用组件来做的,组件负责提升从库为新的主库,代理只负责感知拓扑变化并更新路由。如果没有这类组件,你需要人工登上从库执行:
stop slave; reset slave all;让它脱离复制关系,成为独立主库,然后再把代理的写数据源指向它。整个过程如果稳健,业务感知的抖动可能只有几秒到十几秒。如果超过30秒,多半是应用连接池没有配置好断线重连,或者代理的失效连接回收太慢。
还有一个容易被忽视的细节:切换完成后,旧主库如果重新拉起,它很可能还会以旧的复制关系连过来,造成脑裂风险。所以旧主库恢复后必须确认它是作为新主的从库重新加入拓扑,还是被彻底隔离,不能稀里糊涂放回生产。
4.4 性能衰减与稳定性观测
链路通了,路由也没问题,接下来要回答“代理上线后性能到底打了几折”。我用sysbench做过一组简单对比,测试命令大致是这个样子:
sysbench oltp_read_write \ --db-driver=mysql \ --mysql-host=127.0.0.1 \ --mysql-port=13306 \ --mysql-user=root \ --mysql-password=root \ --tables=10 \ --table-size=100000 \ --threads=16 \ --time=60 \ run直连后端MySQL跑一轮,再把端口改成代理的端口跑一轮,对比两组数据即可。我实测下来,QPS大约下降10%左右,P99从毫秒级缓慢爬升了几毫秒。这个数字仅供参考,不同环境、不同配置差异很大,但它能坚定一个判断:代理的性能损耗可控,远没有想象中那么可怕。
更重要的是压测过程中要观测代理本身。我通常盯四个指标:CPU使用率、内存占用、连接数、P99延迟。如果并发线程从16涨到64,代理CPU涨得比MySQL还快,说明解析逻辑或者连接管理有优化空间。如果内存涨上去降不下来,重点查PreparedStatement缓存和连接池是否回收不够及时。
5. 常见问题排查与避坑实录
5.1 连接数打满,所有服务都卡住
现象:数据库和代理都还没到瓶颈,但应用侧开始大量报连接超时;登录代理管理界面一看,前端连接数飙到了上限。
排查方向分两步:先看是不是连接收敛失效。有些代理在后端配置了很大的连接池,导致数据库节点被创建的连接数其实不少;再看是不是后端某个慢查询把连接池里的连接全部占住,新请求只能排队等连接。这时候用show processlist看后端,通常能看到一堆Sleep时间很长的连接或者正在执行的慢SQL。
解决思路:把代理的“最大前端连接数”设置成合理的值,并且在后端连接池里设置连接空闲超时,别让连接无限期占用。更关键的是排查应用侧连接池配置,如果每个应用实例maxTotal都开得特别大,即使有代理也扛不住瞬间并发。
5.2 无分片键的查询变成广播
现象:某条线上SQL执行时间突然从几毫秒变成几百毫秒,后端所有分片节点的CPU同时出现尖峰。
这种问题基本都是SQL没有带分片键导致的。代理拿不到路由依据,只能全节点广播再合并结果。数据量小的时候无所谓,数据量一大,广播查询就是一场小型灾难。
排查手段是看代理的执行日志,里面通常会标注目标节点或者路由类型。如果是广播,就要推动业务改造SQL,让查询条件包含分片键;如果实在改不了,再考虑建立全局索引表或者使用别的存储来承接这类查询。
这里我有一条经验:上线分表之前,就把常见查询的SQL清单梳理一遍,逐一确认是否携带分片键。不要在分表之后才让业务去查“为什么我的SQL变慢了”。
5.3 读从库读到旧数据
现象:应用刚写完数据,立刻读,偶尔读不到;过一会儿再查就正常了。这种偶发问题最容易让开发同学怀疑人生。
原因通常出在读写分离路由策略上。代理默认会尝试把不在事务里的SELECT路由到从库去减轻主库压力,但“不在事务里”不等于“允许读延迟”。如果你刚在主库写入,紧接着发起的读被路由到延迟中的从库,复制延迟一出现,脏读就发生了。
解决方式:对一致性要求高的接口,要么把整段逻辑包进事务,让代理内部强制所有SQL走主库;要么在SQL里加一个强制主库的Hint,比如ShardingSphere-Proxy就支持通过Hint注解指定路由规则。还有一招是把对应读从“从库路由范围”里剔除。
另外,监控从库延迟不能只看秒级。show slave status里的Seconds_Behind_Master在MySQL 8.0里是秒级精度的,如果网络抖动或者大事务回放,这个值会瞬间跳高。有条件的话记录一段时间内的延迟曲线,对比业务报障时间段,能快速定位是不是复制延迟引发的。
5.4 代理CPU偏高,但QPS并没有明显增长
现象:代理节点CPU持续在80%以上,但观察到的QPS并不高,后端的压力也不大。
这种情况大概率是代理做了大量“额外工作”。常见因子有这么几个:SQL重写逻辑过于复杂,每次请求都要重新做语法树解析;开启了全量SQL审计,每条SQL都要写日志;PreparedStatement缓存没有生效,同一个SQL反复解析。
我遇到过最夸张的一次是某团队把代理的“慢SQL日志”阈值设成了0,等于任何SQL都记录,结果日志刷屏把磁盘IO拖垮,CPU和IO双双告警。最后把阈值恢复到1秒,问题立刻缓解。
排查思路很简单:先关掉一切不影响核心功能的高级特性,再一个特性一个特性打开压测,看CPU曲线的变化拐点。找到元凶之后,要么优化SQL,要么调整特性参数,不要一上来就加机器。
5.5 实战避坑表整理
| 问题 | 常见原因 | 快查手段 | 解决建议 |
|---|---|---|---|
| 连接数突增后雪崩 | 应用重连风暴、代理连接池过大 | 查看代理连接数曲线、后端连接数 | 限制前端连接上限、设置空闲回收、压测重连场景 |
| SQL整体变慢 | 分片键缺失、广播查询、结果集合并 | 代理执行日志、后端CPU曲线 | 补全分片键、拆SQL、优化路由 |
| 刚写完读不到 | 从库复制延迟 | 从库延迟监控、事务路由日志 | 事务内强制主库、Hint强制路由 |
| 代理CPU高 | 审计日志、复杂解析、无缓存 | CPU热点分析、开关特性对比 | 关审计日志、配置Statement缓存 |
| 切换后业务报错 | 旧连接未断开、应用重连不及时 | 代理连接日志、应用报错时间点 | 调整失效连接回收、应用连接池重试 |
写在最后:代理层这条路,怎么走才不亏
做分布式数据库访问层这几年,我最大的体会是:代理不是万能入口,也不是越复杂越好。它不是让数据库变快的魔法,而是让你在数据库规模变大之后,仍然能对访问流量进行统一治理的一道闸门。
如果你正准备引入代理,我的建议是阶梯式演进。先通过代理解决连接收敛和读写分离,把稳定性和体验跑出来;等业务量真正到了一定规模,再逐步引入分片路由、分布式事务等高级能力。不要一开始就上一套大而全的方案,否则排障时多出来的复杂度会把你埋掉。
还有一条经验值得单独提醒:代理上线前,全量SQL日志默认关闭,真需要审计的时候再临时打开,用完了立刻关。我亲眼见过一个代理节点因为审计日志直接把CPU烧到100%,而业务侧只是偶尔有一次慢查询。这类问题不亲身踩一次,光看配置文档是感觉不到疼的。
技术选型和架构设计从来不是纸上拼概念,最终都要回到线上那一张张告警图、一条条报错日志里去验证。希望这些踩坑经验能帮你把分布式数据库代理的路走得更稳。