数据库中间件这词儿,在招聘JD里翻来覆去出现,可真能把它讲明白的文章并不多见。我最近在复盘一套电商类系统的数据层改造时,又把这整条链路重新走了一遍:从最基础的连接管理,到读写分离,再到分库分表、分布式事务,最后落到全局协同。说实话,绝大多数团队并不是一开始就需要一套完整的数据库中间件,而是被线上故障推着走到这一步的——连接被打爆、主库扛不住读流量、单表数据量涨到影响写入、跨库事务不知道怎么处理。每一层都是新问题,每一层都有对应的中间件能力去承接。
这篇文章我不打算罗列一堆产品清单,而是从“连接管理到分布式协同”这条主线出发,把数据库中间件到底在解决什么、每个环节的核心机制是什么、选型时容易踩哪些坑,一层层拆开讲。适合正在做数据层架构升级的后端开发、即将引入中间件的技术负责人,以及想系统理解中间件原理的DBA同学。
1. 连接管理:数据库中间件的第一个坎
1.1 连接风暴是怎么发生的
很多人以为数据库连接只是“应用连上数据库”这么简单,但实际生产环境里,连接往往是第一个失控的环节。我见过不少线上事故,底层的导火索都是连接管理出了问题。
一个典型场景是这样的:业务高峰期,应用侧所有实例的连接池被占满,新请求拿不到连接,大量超时报错。DBA那边看到的数据库日志却是“too many connections”,数据库连接数已经到了上限。两边都在报错,但根因不是同一个:应用池子太小,数据库max_connections又不够,中间还夹着几个慢SQL一直占着连接不释放。
更隐蔽的是短连接风暴。某个服务重启后,所有实例同时初始化连接池,一边建连一边断连,数据库要频繁处理握手、鉴权、分配资源。瞬间几千个连接打进来,直接把数据库打懵。这种情况在容器调度频繁、实例弹性伸缩的架构里尤其常见。
数据库中间件的第一个价值,就是把“连接”这件事从每个应用各自为政,收敛成一个统一入口。它负责连接池的全局管理、连接复用、超时控制、探活检测,甚至可以在数据库节点异常时快速摘除,避免应用继续往坏节点上打连接。这一层不处理好,后面所有功能都无从谈起。
1.2 连接池参数不能照抄默认值
连接池参数属于“平时没人看,一出事全怪它”的典型。我见过太多团队直接把默认配置丢上去,高峰一到就歇菜。这里给出一组基于常见压测和线上反馈总结的经验参考值,具体还要结合自己的业务压测调整:
| 参数 | 作用 | 经验建议 |
|---|---|---|
| maximum-pool-size | 连接池最大连接数 | 不是越大越好,过大会给数据库带来无谓的连接开销 |
| connection-timeout | 获取连接超时时间 | 建议3000ms左右,太短容易误报,太长会拖垮请求线程 |
| idle-timeout | 空闲连接回收时间 | 60000ms附近比较稳妥 |
| max-lifetime | 连接最大生命周期 | 建议小于数据库wait_timeout,避免被服务端断开后继续使用 |
| connection-test-query | 获取连接前的探活SQL | 用SELECT 1即可,不要用复杂查询 |
关于最大连接数,很多人的第一反应是“开大点,免得不够用”。但我实际测下来,连接数并不是越多越好。每个连接都要占用数据库的线程、内存、锁资源,连接数无限调大反而会让数据库整体吞吐下降。连接池的经典估算口径是“CPU核心数 × 2 + 有效磁盘数”,这可以作为下限参考,上限得靠压测曲线来确定。
连接池还有一个容易被忽略的点:连接在数据库端的wait_timeout。如果连接池的max-lifetime设置得比数据库的wait_timeout还大,就会出现连接已经从服务端被断开、但客户端不知道、下次请求报错的情况。这正是连接池需要定期做探活和生命周期回收的原因。
注意:探活SQL能通过,不代表连接真的健康。如果数据库已经处于高负载状态,SELECT 1依然能返回,但实际业务查询却会很慢。所以连接层治理只是第一道关卡,后面还需要数据访问层的路由和熔断能力来兜底。
1.3 从单机连接池到全局接入层
单机连接池解决的是“一个应用实例怎么管好自己的连接”,但数据库中间件解决的是“一组应用实例怎么共享数据层资源”。
举个容易理解的例子:没有中间件时,每个应用都自己维护连接池,20个实例就是20份互不相干的连接配置,哪个实例把连接打满了都不知道。有了中间件,连接被统一收敛到接入层,应用不再直接和数据库建连,而是和中间件建连。中间件再以后端连接池的形式与数据库通信。这样应用侧的连接数量被压缩到很小的范围,稳定性大幅提升。
这块也是中间件“从连接管理到分布式协同”的起点:连接不只是“建连、断连”,还包括会话保持、SQL预处理缓存、事务边界识别、优雅下线等能力。把这些能力集中到一层之后,后续的读写分离、分库分表、限流熔断才能有统一的抓手。
2. 读写分离:路由决策才是核心
2.1 主从拓扑解决了读压力,也带来了新问题
读写分离的逻辑很直观:主库承担写入,从库分担查询,一主多从把读能力横向扩出去。我们通常说“数据库性能瓶颈先从读开始”,所以读写分离是很多团队引入中间件的第一步。
但“配好主从复制”和“做好读写分离”之间还差着一大段路。最典型的就是主从延迟带来的数据不一致。线上经常出现这样的反馈:用户刚提交了一个订单,刷新页面却看不到,投诉“数据丢了”。实际上数据没丢,只是从库还没同步到。
主从延迟的产生原因很多:从库硬件配置低于主库、大事务在从库回放时间过长、从库自身承担了过重的分析查询等。中间件在做路由决策时,必须把延迟因素考虑进去。常见的做法是持续监控主从延迟时间,超过阈值(比如2秒)后,将读流量临时切回主库,等延迟恢复再切回从库。
这里有一个容易误导新人的点:用SHOW SLAVE STATUS看到的Seconds_Behind_Master数值并不总是准确。在主库写入压力极大、从库IO线程卡住等特殊情况下,这个值可能为0,但数据其实还没追平。更可靠的方式是比较主从当前执行到的位点,或者GTID集合的差异。中间件内部做延迟判断时,建议以后者为准。
2.2 事务边界和强制主库路由
读写分离中间件做SQL路由时,不是简单“SELECT走从库,其他走主库”就完事了。有几个特殊场景必须强制走主库,漏掉一个都可能出线上事故。
第一个是事务内的读写。一个事务里哪怕第一条语句是SELECT,只要事务还没提交,后续可能就有INSERT、UPDATE。如果把事务内的读请求路由到从库,事务隔离性、可见性都会出问题。所以正确做法是:一旦识别到事务的开始,整个事务的所有SQL都固定到主库执行。
第二个是“先写后读”的强一致场景。比如支付成功后立刻查询余额,这类查询对延迟极度敏感,就算中间件检测到从库延迟很低,也不建议冒险。很多中间件提供了Hint机制,允许在SQL里显式指定走主库,比如在SQL注释中标记强制主库路由。业务方要根据自己的一致性要求决定是否使用。
第三个容易被忽略的是带有用户会话状态的请求。比如登录态校验后要立刻读取该用户的最新资料,如果路由到延迟中的从库,就会读到旧数据,导致会话状态异常。这类请求建议从入口层面就统一加Hint或者走独立数据源,不要依赖中间件猜测。
2.3 从库负载均衡和故障摘除
读流量落到多个从库上,怎么分配也需要策略。轮询是最简单的,能保证均匀,但如果从库配置高低不同,轮询反而会让慢节点拖累整体延迟。随机策略在请求量大的时候也能趋于均匀,实现更简单。更精细的做法是权重路由,给高配从库分配更大的流量比例。
中间件还需要持续评估每个从库的健康状态,不只是“能不能连上”,还包括连接数、线程运行数、复制延迟等指标。当某个从库的threads_running持续偏高,意味着它已经处理不过来,中间件要主动降低它的权重甚至摘除。等它恢复后,再平滑地将流量切回去,避免一恢复就瞬间被打满。
注意:即使是最成熟的中间件,也不会自动识别所有SQL的语义。遇到子查询嵌套、UNION、聚合函数等复杂SQL时,改写和结果归并的复杂度会大幅上升。尤其是
SELECT ... FOR UPDATE这类带锁读,绝大多数实现会强制路由到主库,因为从库通常没有可加锁的行版本语义。这一点在业务设计时要提前评估,不要等到上线后才发现某个复杂查询根本无法走从库。
3. 分库分表:分片键选错,后面全在还债
3.1 分片键怎么选
当单表数据量持续膨胀,索引维护成本、写入锁竞争、查询扫描成本都会急剧上升。这时候就要考虑分库分表。但分库分表最怕的不是技术实现,而是分片键选错。分片键一旦定下来,后续所有查询都受它约束,改起来比登天还难。
分片键的选择通常要满足三个条件:高频查询的条件字段、分布足够均匀、写入时不产生热点。以订单场景为例,常见的选择有订单ID、买家ID、卖家ID。选买家ID,买家维度的查询就很舒服;但如果平台有大量“按卖家查订单”的需求,买家ID分片就会导致查询打到所有分片,性能退化严重。
拆分的具体算法也会直接影响后续演进。取模分片最简单,数据分布均匀,但扩容时要迁移的数据量很大。范围分片按ID段或时间分片,扩容友好且能方便地做冷热分离,但容易出现尾部热点。一致性哈希能减少扩容时数据迁移的比例,但实现复杂度更高,还要处理虚拟节点和分布不均的问题。
| 分片策略 | 数据均匀性 | 扩容成本 | 热点风险 | 适用场景 |
|---|---|---|---|---|
| 哈希取模 | 好 | 高,全量迁移 | 低 | 数据量大、访问均匀 |
| 范围分片 | 差,尾部倾斜 | 低,追加节点 | 高 | 时间序数据、归档场景 |
| 一致性哈希 | 较好 | 中,部分迁移 | 中 | 频繁扩容、节点变化多 |
3.2 SQL改写与结果归并的代价
分库分表之后,业务代码写的是单表SQL,但中间件要把它改写成多条SQL,分别发到不同分片执行,再汇总结果。这个过程对性能的影响,很多人一开始没有概念。
先说简单查询。SELECT * FROM order WHERE buyer_id = 10086,中间件根据分片键计算,能精确定位到某一个分片,直接执行,性能没有损失。但如果没有分片键的查询,比如SELECT * FROM order WHERE status = 1 ORDER BY create_time LIMIT 20,中间件只能把SQL发到所有分片,各分片各自排序取前20条,再汇总做全局排序。当分片数量很多时,这种查询的消耗是指数级上升的。
最坑的是深分页。LIMIT 100000, 20这种写法,在单表只是偏移大一点,在分库分表环境下会变成:每个分片都要取前100020条,全部返回给中间件,再在内存里排序后取出第100000到100020条。数据量一大,中间件内存直接爆掉。
业界常见的优化手段有两种:一是改为游标分页,基于上一页的最后一条排序值往下翻,比如WHERE create_time < ? ORDER BY create_time DESC LIMIT 20,这样每页的扫描范围都很小;二是对于必须跳页的场景,先查询出主键集合,再用主键回表查询详情。无论哪种,都要在设计阶段就想清楚,不要等分页性能崩了再急。
跨分片的聚合也需要注意。COUNT、SUM这类聚合,中间件会先在各分片做局部聚合,再汇总得出最终结果。总体思路没问题,但前提是聚合语义能被正确拆解。AVG就不能简单对各分片AVG后再求平均,必须改为各分片分别返回SUM和COUNT,再由中间件精确计算。中间件处理这些边界时,实现细节差异很大,选型时不要只看宣称支持多少种SQL。
3.3 扩容迁移最容易翻车
分库分表上线后,扩容是早晚要面对的问题。取模分片下,节点数从4个扩到8个,绝大多数数据的映射关系都变了,必须做数据迁移。
最稳妥的是停机迁移,维护窗口内停写,启动迁移任务,把数据按新规则分发到新节点,校验一致后切换路由。但很多业务做不到长时间停写,只能走在线迁移。在线迁移的常见套路是双写:新旧两套存储同时写入,增量数据通过订阅主库变更日志同步到新节点,存量数据用批处理回放。这个过程的难点在于两条链路的延迟和数据一致性校验。
我见过不少团队在迁移过程中因为“先切读、再切写”的顺序搞错而出问题。正确的节奏一般是:双写一段时间,确认两边数据一致;先切读流量到新集群,验证性能和正确性;最后再切写流量。切写之后保留旧集群一段时间,作为回滚逃生通道。如果新旧规则要共存,中间件需要支持按比例灰度路由,而不是一下子全量切过去。
经验:分库分表方案在实施前,一定要先做“分片键覆盖度”分析。列出业务里所有核心查询SQL,确认它们要么能通过分片键精确定位,要么能接受全分片扫描的代价。如果大量查询都依赖非分片键字段,那说明分片键选错了,或者应该引入二级索引/全局索引表来支撑。
4. 分布式协同:事务、全局ID与锁
4.1 XA为什么在互联网场景里不讨喜
数据被拆分到多个数据库之后,跨库事务成了绕不开的问题。最早形成标准的方案是XA,基于两阶段提交。第一阶段prepare,所有参与者把事务资源锁住并准备提交;第二阶段commit,协调者通知各参与者正式提交。
XA的强一致性模型看起来很完美,但高并发场景下问题明显。两阶段提交期间,参与事务的数据库记录会一直持有锁,导致其他请求被长时间阻塞。一旦协调者在prepare之后宕机,事务就陷入悬挂状态,没有参与者能自行决定提交还是回滚,只能等协调者恢复。这种不确定性,在互联网高可用架构里很难接受。
所以现在的分布式事务方案,几乎都走向了“最终一致性”。最终一致不是放弃一致性,而是通过补偿、重试、对账等手段,在短暂的不一致窗口之后达到一致状态。中间件在其中的角色,是屏蔽底层的协调、重试、幂等机制,让业务代码只关注自己的业务规则。
4.2 TCC、SAGA、本地消息表怎么选
常见的分布式事务方案大致有三类,各自适用场景差别很大。
TCC模式把一次业务操作拆成Try、Confirm、Cancel三个阶段。Try阶段做资源预留和检查,Confirm阶段真正执行业务,Cancel阶段回滚。TCC的优点是可以做到较严格的最终一致,业务可控制性强;缺点是侵入性非常大,每个参与方都要实现三套逻辑,还要处理幂等和空补偿。
SAGA模式把一个长事务拆成一系列本地事务,每个本地事务都有对应的补偿事务。执行过程中一旦某个步骤失败,就逆序执行补偿操作回滚。SAGA更适用于跨系统、长流程的业务,比如旅行预订这类涉及多个外部服务的场景,但需要事务协调器来管理状态流转。
本地消息表方案则相对轻量,核心思想是把“发消息”和“写业务数据”放在同一个本地事务里,消息写入消息表,事务提交后消息队列异步投递,消费方处理成功后更新消息状态。这个方案对MQ的依赖低,实现简单,适合大多数异步最终一致的场景,缺点是无法做到强一致且需要额外的对账清理任务。
| 方案 | 一致性 | 业务侵入 | 实现复杂度 | 典型场景 |
|---|---|---|---|---|
| TCC | 较高,偏实时 | 高 | 高 | 资金类、库存类 |
| SAGA | 最终一致 | 中 | 中高 | 跨系统长流程 |
| 本地消息表 | 最终一致 | 低 | 中 | 异步通知、解耦 |
中间件做的分布式事务,本质上就是把这些模式固化成通用能力,提供幂等控制、补偿调用、状态查询、超时处理等基础组件。业务侧不需要重复造轮子。
4.3 全局ID和分布式锁:被低估的协同能力
“分布式协同”这个词,落在数据库中间件上,除了事务还有两个高频能力:全局唯一ID和分布式锁。
全局唯一ID在分库分表后是刚需,因为单库自增主键在分片下会产生重复。号段模式是主流做法之一:中间件从数据库批量获取一段ID区间,应用在本地按顺序分配,用完再取下一段。这种模式性能和数据库压力都得到平衡。雪花算法也很流行,但要注意时钟回拨问题,回拨可能导致ID重复,中间件需要一定的时钟容错机制。
分布式锁在跨库、跨实例场景下,实现方式五花八门。有人用Redis的SETNX加过期时间,有人用协调服务的临时顺序节点。常见的坑集中在锁的续期和误删上:业务执行时间超过了锁的过期时间,锁自动释放,其他实例拿到锁进入临界区;又或者线程A执行完释放锁时,线程B的锁已经生效,A把B的锁删了。中间件提供统一的锁SDK,可以内置续租、安全释放、可重入等机制,避免每个团队各自实现一套,各自踩一遍坑。
5. 高可用兜底:探活、限流与全链路观测
5.1 健康检查的“假活”问题
数据库中间件的核心价值之一是高可用管理,但对“健康”的判断标准不能停留在“能连通”。
最简单的探活方式是SELECT 1,但它只能证明数据库进程还在,TCP连接还能握手。如果数据库已经因为负载过高导致查询排队,SELECT 1依然能成功,因为这种查询几乎不消耗任何资源。等中间件判定“健康”并把流量持续打过去,真正的业务SQL就会大面积超时。
所以生产级的探活必须分层设计。第一层是网络连通性和进程状态,第二层是轻量级业务查询,比如访问一张极小表的当前状态,第三层是资源指标采集,包括CPU、内存、磁盘IO、活跃连接数、复制延迟等。中间件需要综合这些指标做节点健康评分,再决定是否将节点从可用列表中摘除。
另外要注意liveness和readiness的区别。一个节点可以被标记为“进程存活”,但未必“准备好接受流量”。在数据库实例重启、buffer pool预热、复制追平阶段,中间件应该将节点标记为readiness不佳,暂时不分配流量,避免请求打到还没准备好的节点上。
5.2 限流熔断和降级策略
数据库中间件处在应用和数据库之间,天然适合做流量治理。
限流可以多维度展开:按目标数据库节点维度限制每秒SQL执行数,防止单节点过载;按SQL类型限制,比如全表扫描类的大查询设置并发上限;按业务账号或应用维度限制,防止某个应用异常拖垮整个数据层。
熔断和降级则是中间件应对故障的主动手段。当某个从库延迟过高,中间件自动摘除节点并触发降级,将读流量切到其他从库或主库;当主库写入压力逼近阈值,对非核心写入进行排队或拒绝。更精细的做法是按比例降级,比如某节点一旦出现慢查询比例升高,中间件先切走50%流量观察,而不是直接全量切换。
很多团队容易忽略的一点是:中间件自身的限流和熔断策略,必须和业务方的降级预案联动。比如大促场景下,中间件可以主动将“排行榜查询”这类非核心读接口限流,让出资源给交易链路。这个决策不能只靠中间件随机判断,而是需要业务侧通过开关或配置下发来控制。
5.3 全链路观测:定位瓶颈的唯一出路
数据链路一旦引入中间件,就是“客户端节点-中间件节点-数据库节点”的三层结构。出了问题,最头疼的是定位瓶颈到底在哪一层。
所以中间件必须提供充足的观测能力。每一个SQL请求都应该有一个全局traceId,从应用发出到中间件转发再到数据库执行完成,整条链路串联起来,记录每一跳的耗时、目标节点、影响行数、扫描行数。出问题时,先看是中间件转发耗时高,还是数据库执行耗时高,再去对应层面排查。
我遇到过一种典型场景:数据库load偏高,但直接看慢日志又没有特别离谱的SQL。后来通过中间件下发的全链路信息才发现,某个定时任务在凌晨批量执行了大量小查询,单条都很正常,但数量巨大,把数据库IO队列打满了。这种问题只靠数据库慢日志根本定位不了,必须依赖中间件层面的访问审计和统计能力。
建议:中间件的访问审计日志一定要开,而且要保留足够长的时间。数据量确实大,但关键时刻它是排查问题的唯一线索。可以只记录扫描行数超过阈值、或者执行时长超过阈值的SQL,降低存储成本。
6. 部署形态:代理式还是客户端式
6.1 两种模式的天平两端
数据库中间件的部署形态,大体分成代理模式和客户端SDK模式两类,没有绝对的好坏,只是选择不同。
代理模式是独立部署一套中间件集群,应用连接中间件,中间件再连接数据库。对应用来说透明,应用不需要引入任何特殊SDK,支持的语言天然可以对接。代价是多一跳网络转发,性能损耗大约在5%到10%之间,和中间件进程的编码效率、序列化方式强相关。
客户端SDK模式是把中间件能力以SDK形式嵌入应用进程,应用直接连接数据库,中间件逻辑在本地完成路由和改写。优点是少了一跳网络延迟,路由决策可以做得更精细;缺点是语言绑定严重,Java全家桶还好,多语言团队会非常痛苦,而且SDK版本的升级要跟着应用一起发版,治理成本高。
| 维度 | 代理模式 | 客户端SDK模式 |
|---|---|---|
| 性能损耗 | 多一跳网络转发,有一定损耗 | 本地处理,损耗低 |
| 多语言支持 | 协议层对接,天然支持 | 每个语言都要实现SDK |
| 部署运维 | 独立集群,集中管控 | 随应用发布,分散难以统一 |
| SQL能力 | 受协议解析限制 | 可以深度定制,贴近业务 |
| 故障影响面 | 中间件集群故障影响所有应用 | 单个应用故障不会波及其他应用 |
6.2 选型前先问自己三个问题
第一问:团队语言栈是不是足够统一。如果全是Java,客户端SDK会有明显优势;如果团队有PHP、Go、Python多个语言,代理模式能省下大量重复SDK的开发和维护成本。
第二问:数据库运维能力够不够强。代理模式意味着又多了一个要7x24小时保障的中间件集群,它本身还需要高可用部署、版本升级、容量规划。如果运维团队本身就吃力,客户端模式反而是更稳妥的选择,少一层要维护的基础设施。
第三问:对延迟和吞吐的要求到底有多苛刻。有些业务对每一毫秒都敏感,多一跳网络转发都不能接受;有些业务本身就有多层级缓存,数据层加一点延迟完全无感。这个没有标准答案,只能用真实业务流量压测后拍板。
从我接触过的项目来看,客户端SDK模式更适合中小团队快速上线能力,而代理模式在规模上来之后,因为可以集中做权限、审计、限流、灰度发布,长期维护成本反而更低。也有团队采用混合架构:核心交易链路用客户端SDK保证性能,分析类、报表类流量通过代理接入,方便统一治理。
6.3 中间件自身也在演进
近两年的趋势是,中间件的“控制面”和“数据面”开始分离。路由规则、分片配置、限流策略、分布式事务协调这些逻辑被抽到一个中心化控制面,而实际的SQL转发和改写职责作为一个轻量数据面,既可以选择独立部署,也可以嵌入到代理节点里。配置变更通过控制面下发,数据面动态热加载,不再需要重启集群。
这个趋势实际上让“代理模式和客户端模式”的边界变得模糊。数据面可以是客户端SDK,由中心控制面下发配置;也可以是一组代理节点,由控制面统一管理。对于使用者来说,重要的是先想清楚:我的团队更需要“接入简单透明”,还是“性能极致可控”。这个权衡比纠结具体产品名称重要得多。
7. 落地节奏:不要一上来就追求全家桶
7.1 分阶段演进,别跳跃
很多团队在引入数据库中间件时,最容易犯的错误是一步到位。数据库还没到瓶颈就开始分库分表,单表单表数据只有几百万行就强行拆成十几片,结果业务查询被人为复杂化,所有分片都打一遍,性能比单库还差。
合理的节奏应该是跟着数据和流量走。第一阶段,连接治理和读写分离,这能解决大多数团队的读扩展问题;第二阶段,当单表数据量已经影响写入或索引维护,再引入分库分表;第三阶段,当跨库事务开始成为业务开发的瓶颈,才考虑分布式事务和全局协同能力。每一步都需要明确当前要解决的问题是什么,而不是为了技术先进而提前引入。
分片尤其要克制。分库分表一旦落地,就基本告别了复杂的关联查询和灵活的多维检索。如果业务还处于快速迭代期,字段和查询方式还在频繁变化,过早分片会变成沉重的技术负债。
7.2 预留逃生通道
任何中间件方案都要考虑“如果这套东西不好用,怎么退出去”。这里的退出不是指简单的下线,而是能把路由规则、分片元数据、配置中心的数据导出,能将SQL执行链路降级回直连数据库模式。
选型时也要关注中间件的定制程度。有些团队喜欢在开源中间件上做深度二次开发,加了很多私有逻辑,等上游发版时发现无法合并,只能长期维护一个分叉版本。这个长期维护成本远超最初节省的那些开发时间。更稳妥的做法是:尽可能使用主流的通用能力,私有定制收敛到配置插件层面,而不是改动核心路由逻辑。
7.3 我最看重的几条经验
排查过很多数据层问题之后,我最大的感受是:中间件不是灵丹妙药,它只是把复杂度从应用层收敛到数据层。复杂度不会消失,只会转移。如果业务侧对数据一致性、分片键选择、事务边界这些问题没有清晰定义,再强的中间件也救不了。
另一个体会是:规则越简单,系统越稳定。分片规则能用取模就不要用一致性哈希,路由规则能显式声明就不要依赖中间件智能猜测。数据库中间件最怕的不是功能少,而是规则复杂到没人能预测某条SQL到底会走到哪个节点。复杂度每上升一点,出问题的概率就会指数增长。
我后来在几个项目里反复建议团队:先把连接管理做扎实,把主从路由的边界划清楚,再考虑分库分表和分布式事务。绝大多数团队在完成前两步之后,就已经解决了80%的数据层问题。分库分表和分布式协同,是等业务规模真正逼到那一步再说的事。这听起来不够“炫技”,但恰恰是数据库中间件落地最稳妥的路径。