数据库分库分表后,我的系统从“慢”变成了“又慢又复杂”
当前位置:点晴教程→知识管理交流
→『 技术文档交流 』
如果你没听过银行系统,你可以把它理解成一个超级收银台。只不过这个收银台不能重启,不能出错,半夜还得自动结账,早上开门前还得把账算得一分不差。 就这么一个祖宗,最近被我“优化”了。 怎么优化的呢?分库分表。 起因很简单——数据库扛不住了。每天几千万笔交易,单表三亿条数据,一个简单的余额查询,慢得像在海底捞针。DBA天天发邮件说“主库负载过高”,老板天天问“系统啥时候崩”,我天天做梦都在加索引。 行,分吧。 结果呢?系统从“慢”变成了“又慢又复杂”。 【1.全局主键:我连自己家的门牌号都找不到了】 分库分表之前,主键用的是数据库自增ID,省心省力,岁月静好。 分完之后,发现每个库每个表都从1开始自增。订单表里出现了几十个ID为1的记录。前端查单号,后台一脸懵:您说的是哪个库的1号? 于是我们上了分布式ID生成器,雪花算法,64位长整型,全球唯一,理论上一万年不重复。 结果呢?有一次凌晨跑批,系统突然报错:主键冲突。 查了半天,发现是服务器时钟回拨了0.01秒,雪花算法生成了两个一模一样的ID。 你能想象吗?全球唯一的ID,输给了服务器的时间不准。 那一刻,我深刻体会到一句话:分布式系统里,最难的不是分,是“唯一”。 【2.跨库Join:一顿操作猛如虎,一看代码全在哭】 以前查一笔交易,连带上客户信息、账户信息、产品信息,一个SQL搞定,三张表join一下,舒舒服服。 分库分表之后,客户信息在库1,交易流水在库2,账户余额在库3。 再想join?对不起,数据库不支持跨库join,除非你用联邦查询,但那玩意儿慢得你想骂人。 没办法,只能改代码。 原来一个SQL的事,现在变成: 先查交易流水,拿到客户ID 再去客户库查客户信息 再去账户库查余额 最后在应用层拼装数据 三个接口调用,三次网络开销,三次可能的失败重试。 原来200毫秒的事,现在2秒起步。用户在前端转菊花,我在后台转焦虑。 更崩溃的是,有一次其中一个库超时了,整个接口直接挂了。老板问:为啥查个交易记录都能崩? 我解释:因为分库了,跨库join需要调用多个服务,任何一个出问题都会导致…… 老板打断我:我就问你是不是比之前慢了? 我:……是。 老板:那你说个屁。 【3.分布式事务:钱转走了,余额没扣】 银行系统最怕什么?钱对不上。 以前单库单表,事务用begin/commit,要么全成功,要么全回滚,稳得像老狗。 分库分表之后,A账户在库1,B账户在库2。A给B转100块,库1扣款成功,库2入账失败。 钱没了。 这就是分布式事务的经典场景:部分成功,部分失败。 我们上了TCC事务框架,Try、Confirm、Cancel三步走,理论上是完美的。 实际跑起来呢?有一次网络抖动,Try阶段成功了,Confirm阶段超时了,框架自动触发Cancel。结果Cancel的时候,另一个节点刚好在重试Confirm,两边同时执行,数据直接乱套。 最后对账发现,有一笔钱被扣了两次,入账一次。客户投诉:我转100,对方收到100,我扣了200。 你能怎么解释?说系统太智能了,自动帮你多转了一份? 从那以后,我对分布式事务的态度是:能不用就不用,实在要用,就祈祷网络永远不抖。** 【4.分片键选错:我把自己分成了孤儿】 分库分表最关键的是选对分片键。比如按客户ID分,同一个客户的所有数据都在一个库,查询起来美滋滋。 我们当时拍脑袋选了按账号分,因为交易流水大多按账号查,看起来没毛病。 结果呢?运营部门有一天说要搞全行大促,需要统计所有客户的交易总金额。 一个简单的group by,现在变成:遍历所有分表,把所有数据拉回来,在应用层聚合。 64张表,每张表几千万数据,全量扫描,应用内存直接炸了。 最后只能搞离线跑批,凌晨四点跑完,早上八点出报表,运营的人上班一看,数据还是昨天的。 他们问我为啥不能实时?我说因为分库了。 他们说:分库不是为了更快吗? 我沉默了。 【5.扩容:我以为分了就一劳永逸,结果还要再分】 分库分表的时候,我们预估业务增长,定了32个库,每个库64张表,心想够用五年。 结果两年不到,流量翻了三倍,有的表又破亿了。 想扩容?没门。 分库分表的路由规则是写死在代码里的,hash算法固定死了,加库意味着重新hash,所有数据都要重排。 我们研究了三天,最后决定:新建一套128库的集群,然后双写三个月,等数据同步完了再切流量。 那三个月,每次上线我都心惊胆战。有一次双写出bug,新老库数据不一致,对账差了十几万,我们三个人熬了两个通宵,一条一条手工补数据。 分库分表,就像买房,你以为上车了就安稳了,结果发现孩子上学还得换学区。 【6.总结:分库分表不是银弹,是“银弹头”】 回头看,分库分表确实解决了单库的性能瓶颈,但也带来了十倍以上的复杂性。 它把单点问题变成了分布式问题,把数据库问题变成了架构问题,把SQL问题变成了代码问题。 有人问,那到底该不该分? 我的建议是:能不分的尽量不分,实在要分的,先做好心理建设。 你要接受一个事实:分库分表之后,你的系统不会变得“又快又稳”,而是会变得“又慢又复杂”——但至少它能扛住流量了。 就像治脱发,你剃了光头,确实不用再担心掉头发了,但你会开始担心头皮过敏。 我现在就是这样,每天顶着光头,一边挠头皮,一边盯着监控,祈祷不要出幺蛾子。 最后送大家一句话: 分库一时爽,维护火葬场。 要想不分库,先把索引建。 非要分的话,劝你三思而后行。 阅读原文:原文链接 该文章在 2026/3/9 9:44:44 编辑过 |
关键字查询
相关文章
正在查询... |