数据库这玩意儿,在微服务架构里,从来都不是一个技术选型问题,而是一个组织问题、一个权力分配问题。你搞微服务,第一步就得面对那个盘踞了七八年的老库,几百张表挤在一起,像大学宿舍一样混乱。我见过太多团队,服务拆得挺利索,结果数据库还抱成一团,所有服务都往一个库里怼,这叫伪微服务,是自欺欺人。真正的拆分,是从你决定哪个服务拥有哪张表的所有权那一刻开始的。这活儿干得好,系统能多活五年;干得稀烂,线上故障比你的发际线后退得还快。

先别急着动刀子,你得先摸清楚现状。最笨也最有效的办法,就是画一张表依赖图——不是画ER图那种理想化的玩意儿,而是跑一遍全链路的SQL日志,看看谁在查谁的表。你会发现,所谓的“订单服务”其实在偷偷读“用户表”的余额字段,“支付服务”居然在改“库存表”的某个状态。这种耦合,靠代码review根本揪不出来,只有日志不会撒谎。摸完底,你得把这些交叉访问列个清单,然后挨个找对应的服务负责人谈:这张表到底是你的还是我的?谈判的过程往往比技术难,因为牵扯到KPI,牵扯到谁不想动自己的代码。但这一步绕不过去,你不把所有权理清楚,后面拆库就是拆雷。
所有权理清了,接下来是拆分策略的选择。我见过三种主流打法,各有各的坑。第一种是“按业务域硬拆”,订单域一个库,用户域一个库,库存域一个库。听着清爽,但现实是业务域之间的边界从来不是一刀切的,比如“购物车”到底算订单域还是用户域?你硬拆,就得引入大量的跨库查询,性能瞬间拉胯。第二种是“按数据热度拆”,热数据放一个库,冷数据放一个库,这个对读写比悬殊的系统挺管用,但问题是业务逻辑会被撕裂,一个服务要同时连两个库,代码里全是if判断。第三种是我比较推荐的,叫“按服务能力拆”,先拆那些读多写少、独立性强的基础服务,比如字典服务、配置服务,它们的库先独立出去,风险最小,收益立竿见影。
拆库的时候,有个技术细节特别容易被人忽略——分布式事务。你原来在一个库里用本地事务,改两个表,要么都成功要么都回滚。拆成两个库之后,这事儿就变成了跨库操作,本地事务直接失效。很多人第一反应是上Seata或者Saga,但我要泼盆冷水:能不上分布式事务就别上。你仔细想想,很多所谓的“跨库操作”,其实本质上是两个独立业务动作,只是你以前习惯了用事务把它们绑在一起。比如下单扣库存,这俩其实可以拆成“下单成功”和“库存扣减”两个事件,中间用消息队列异步解耦,库存扣减失败了就补偿,而不是强一致。你要是真上了分布式事务,性能损耗是一回事,最麻烦的是故障排查,你根本不知道是哪个分支事务卡住了。
拆完库之后,治理才是重头戏。我见过太多团队,拆库一时爽,拆完就撒手不管,结果三个月后,新库又长成了大杂烩。治理的第一步是禁止跨库join,这个必须写进代码规范里,用CI检查来卡,发现一条就驳回一次。但禁止join之后,数据怎么拿?你得引入数据冗余和本地缓存。比如订单服务要展示用户昵称,你不能每次去查用户库,而是在订单表里冗余一个nickname字段,用户改了昵称,通过发消息来异步更新。这听起来有点绕,但这是微服务数据治理的基本功。第二步是读写分离,每个核心库必须配只读副本,查走副本,写走主库,这个能扛住大部分的性能压力。
还有一个治理细节,很多人会忽略——数据归档。微服务跑久了,订单表、日志表、流水表,动辄几亿行,索引再优化也没用,因为查询底层扫描的IO在那儿摆着。你得定个策略,比如订单表,三个月前的数据自动归档到冷库,用分区表或者分库的方式,把历史数据挪走。这个动作要自动化,不能靠DBA手工跑,得写进系统的定时任务里。归档之后,热表查询速度立竿见影。但归档也有坑,比如用户突然要查半年前的订单,你得有个统一的查询入口,能自动路由到冷库,这个接口要对业务透明,不然产品经理会找你拼命。
治理的另一个维度是监控与告警。数据库拆了之后,故障定位的难度是成倍增加的。以前一个慢SQL,你看一眼慢查询日志就知道是哪张表。现在服务跨了好几个库,你得先定位是哪个服务出了问题,再顺着调用链找到是哪个库,再查慢日志。所以你得在拆库的同时,把全链路


