先聊个真事。上个月帮一个做在线教育的朋友看数据库,他们的业务库已经涨到接近 9TB,主从复制延迟动不动就飙到十几秒,每天凌晨的大查询和备份任务挤在一起,直接把主库 IO 打满。他问我该不该上一个分布式中间件做分库分表,我劝他先别急——很多团队在这个阶段其实不是缺分库分表的能力,而是缺一个真正能横向扩展存储、又不需要业务大改的底座。这也是我今天想聊的 PolarDB 存算分离架构的典型适用场景:当数据量走到 100TB 这个量级,单机 MySQL 和常规主从已经被压到极限,分库分表又会带来巨大的改造代价和数据搬迁风险时,用存算分离把存储层独立出去、按需扩展,往往是最平滑、性价比也最高的破局方式。
这篇内容我结合三个真实客户的落地案例来拆:他们分别属于游戏、金融科技和电商零售,数据规模全部到了 100TB 上下,业务形态各不相同,但最终都选择了 PolarDB 的存算分离路线。我会把每个客户在切换前的痛点、为什么没有走别的路、最终集群长什么样,以及切换过程中踩过的坑和调整细节都展开讲清楚。对正在做容量规划、数据库选型或者被大表拖垮的运维和 DBA 来说,这篇应该能给你一条比较清晰的参考路径。
1. 存算分离为什么是超大规模数据的必然选择
1.1 传统架构在百TB规模下的困境
先把话说透:在数据量达到 100TB 之前,很多团队其实感受不到架构的瓶颈。单机 MySQL 配上主从复制,撑到 5TB、10TB 都还算体面,顶多是把慢查询优化一下、把归档任务错峰跑一跑。但一旦数据量跨过 50TB 往 100TB 走,问题就不是优化能解决的了。
首先是存储成本的失控。一台物理机配 NVMe SSD,单盘容量和 IOPS 总是有限制的。为了扛住 100TB 的数据量,你可能需要在一片大容量 HDD 上存放冷数据,再把热数据放在 SSD 上,于是就得自己实现一套冷热分层逻辑——这听起来很美,做起来全是泪。其次,主从同步开始变得不可靠。100TB 的实例,binlog 的生成速度非常惊人,从库追主库的延迟会从毫秒级涨到秒级,甚至分钟级。我见过最夸张的情况是某个从库因为大事务回放卡了整整四十分钟,业务方已经打了三次投诉电话。再次,备份和恢复的时间窗口完全失控。用传统物理备份或者逻辑备份跑一次全量,动辄十几个小时,期间对主库的影响又没法完全消除,一旦需要恢复数据,整个团队的心态基本是崩溃的。
这些困境的本质只有一个:计算和存储耦合在一起,导致扩容时计算资源和存储资源必须一起加,数据搬迁成本极高,存储瓶颈直接锁死了整体的扩展能力。
1.2 存算分离的核心思想与收益
PolarDB 存算分离的思路其实不复杂:把 MySQL 兼容的计算节点和底层存储节点拆开,计算节点只负责 SQL 解析、执行计划生成和事务处理,而数据实际存放在一个分布式存储池中。这个存储池内部会自动把数据切成一个个数据分片,分布在多台存储节点上,并且通过多副本机制保证数据可靠性。
这样做带来的第一个直观收益,是扩容方式的改变。以前加存储必须加机器,现在存储空间不够了,直接在存储池上做横向扩容就行,计算节点数量可以保持不变。反过来,如果只是 CPU 或内存不够,那就单纯增加只读计算节点,存储不需要动。这种存储与计算完全解耦的模型,让容量规划变得像在云上点外卖一样简单。
第二个收益是存储成本的显著下降。PolarDB 的存储池底层可以分层放置热数据和冷数据,不需要业务方自己维护一套归档逻辑。对 100TB 级别的数据来说,单是存储成本一项,不少客户算下来就比传统方案节省了一半以上。第三个收益则是高可用能力的增强。存储节点自身通过多副本机制保障数据不丢失,计算节点宕机后可以在秒级内完成切换,并且因为数据在存储池里是共享的,新的计算节点起来后不需要做任何数据回放或重建,这个恢复速度和传统主从架构完全不是一个量级。
对很多团队来说,存算分离最大的隐性收益其实是:它让 DBA 从"每天陪着大实例熬备份"的状态里解放出来,把精力放在真正的业务优化上,而不是天天加班救火。
2. 三个客户的真实落地案例
2.1 游戏行业:支撑千万级日活的用户中心与日志库
第一位客户是国内一家做休闲竞技类手游的厂商,他们的日活峰值在千万级别,玩家数据、对局记录、道具流水和充值流水都集中在这套数据库里。最核心的库是用户中心和结算中心,数据量大概在 60TB 左右,再加上对局日志库和历史归档,总数接近 120TB。
他们切换前用的是自建 MySQL 主从集群,每台物理机挂 12 块 4TB SATA SSD,做了 RAID 10。核心痛点有两个:一个是每个月末的结算批处理任务跑起来后,主库的 CPU 和 IO 直接被打满,玩家在晚高峰时段会出现登录超时;另一个是充值流水表已经增长到接近 8 亿行,单表太大了,每次对账查询慢得让人崩溃,他们甚至考虑过分库分表,但评估下来改造量太大——支付和账号体系的强事务查询根本不支持按用户 ID 简单拆分。
切换 PolarDB 存算分离架构后,他们把用户中心、结算中心和充值流水全部迁到了 PolarDB 的一个大实例上,计算节点用了 8 核 64GB 的主节点加 4 个只读节点,存储池自动扩展到 120TB。结算批处理任务还是照常在凌晨跑,但因为它现在跑在独立计算节点上,主库完全不感知,晚高峰的登录超时问题直接消失。充值流水的大查询由于可以路由到只读节点,对账效率提升了接近十倍。这个客户最满意的一点是,他们保留了大表的原始结构,没有做任何拆分,业务代码改动量几乎为零。
迁移过程中踩过一个比较典型的坑:因为历史归档库里有一部分表用的是 MyISAM 引擎,迁移工具不支持直接转换,后来用了 PolarDB 提供的数据迁移服务做了全量加增量同步,先在目标端把表结构改成了 InnoDB,再开放业务流量,整个过程前前后后演练了三次才正式切流。
2.2 金融科技:交易流水库从扩容噩梦到按需扩展
第二位客户是某头部金融科技公司的核心账务团队,他们的账务流水库是最典型的超高并发写入场景。每天产生的交易流水超过 5 亿条,单日新增数据量在 500GB 到 800GB 之间,整体数据规模接近 100TB。对金融系统来说,数据一条都不能丢,强一致性是刚需,同时还要支持多维度查询,比如按用户查、按商户查、按交易时间范围查,还有大量对账批处理任务。
他们原来的架构已经是 MySQL 分库分表的经典方案:256 个分片分布在 8 台物理机上。这个方案支撑到了 80TB 左右,问题开始显现。分片的数据分布不均衡,有的分片因为某个大商户的流水集中写入,已经膨胀到快 1TB,而其他分片只有 300GB。每次扩容一个分片,都要经历数据搬迁、路由规则变更、灰度验证的完整流程,光是一个分片的迁移就要做三到五天,而且大商户的强事务查询跨了多个分片后,性能下降非常明显。
他们转向 PolarDB 存算分离后,最核心的调整是放弃了分库分表,把核心流水表重新合并回一张大表,直接跑在 PolarDB 的单个大实例上。你没听错,原来在传统架构下必须分库分表的场景,在存算分离的架构下反而不需要了。因为存储池可以横向扩展,单表容量不再是瓶颈,而计算节点通过增加只读节点来分散查询压力。这个改动直接把他们的应用层代码从分库分表中间件的各种限制里解放了出来,跨分片查询变成了普通 SQL,开发效率提升非常明显。
因为这个客户对数据一致性要求极高,他们选择全部写入走主节点,只读节点主要承接对账和报表类的查询。实测下来,在账务流水表 100TB 规模下,单条按条件索引查询的 P99 延迟可以稳定在 20 毫秒以内,这在他们原来的分库分表架构上很难达到——因为跨分片的查询需要聚合,网络开销太大。
2.3 电商零售:订单中台与商品中心的统一数据底座
第三位客户是电商赛道的一家头部平台,他们的数据规模最为庞大。订单中台超过 70TB,商品中心接近 30TB,再加上会员、营销、库存等系统,总量大概到了 160TB。这家公司和前两家不同的地方在于,他们没有历史包袱,从一开始就搭建在云原生架构上,之前的订单库用的是另一个云厂商的托管 MySQL,当时已经遇到了明显的容量上限——单实例存储上限是 16TB,他们不得不把订单表按年拆成多张物理表,业务代码里到处是年和月参数拼接的表名,开发同学叫苦不迭。
他们换 PolarDB 的时候做了一个很彻底的决策:不搞按年拆表了,直接合并成一张全量订单大表。因为 PolarDB 存算分离的存储池可以扩展到 100TB 以上,单表容量上限被彻底打破。现在订单中台和商品中心统一跑在 PolarDB 集群上,主计算节点规格是 16 核 128GB,外加 6 个只读计算节点,分别承接订单查询、商品详情读取、运营报表等不同的业务流量,实现了读写完全隔离。
商品中心有个很典型的场景:大促期间,商品价格和库存需要频繁更新,同时商品详情页的并发查询量会突然暴增。以前他们在托管 MySQL 上只能靠增加只读副本扛查询压力,但只读副本有延迟,经常出现用户看到的价格和库存不是最新值的情况。换到 PolarDB 后,因为存储是共享的,只读节点天然强一致,用户可以读到主节点最新提交的数据,大促稳定性提升非常明显。这个客户在双十一大促峰值期间,订单中台的写入 TPS 稳定保持在每秒 12 万以上,查询 QPS 超过 80 万,存储池的 IO 延迟全程没有超过 3 毫秒。
3. 存算分离架构下的核心细节与实操要点
3.1 存储引擎与计算节点解耦后的写入链路
很多刚接触 PolarDB 的同学会习惯性地把它的存储池想象成一块普通的网络硬盘,其实这是个重要的理解偏差。PolarDB 的计算节点和存储节点之间采用了一套专门设计的传输协议,计算节点上的 InnoDB 引擎在写入数据时,并不是像传统架构那样先把数据页写到本地磁盘,再通过 binlog 同步给其他节点,而是把 Redo Log 实时下推到存储池,由存储节点负责把数据页最终落地。
这个设计带来的直接效果是:主节点和只读节点共享同一份存储数据,binlog 的同步压力被彻底消除。你在 PolarDB 集群上创建只读节点,它不需要像传统 MySQL 从库那样去回放主库的 binlog,而是直接读取共享存储上已经提交的数据。只读节点的扩展速度因此变得非常快,而且只读节点永远不会有复制延迟超标的问题,因为它的数据已经是最新的。这也是前面提到的电商客户能在只读节点上做到强一致读的根本原因。
实操层面有个重要的调优点:写入密集型的业务要特别关注 Redo Log 的刷盘策略和存储池的 IOPS 容量。PolarDB 的每个计算节点可以独立设置 innodb_flush_log_at_trx_commit,如果业务对数据安全性要求极高,就保持默认的 1;如果是一些可以容忍小概率丢失的日志型业务,可以改成 0 或 2,这样写入吞吐会明显提升。我自己在压测环境下对比过,在 100TB 数据量下把刷盘策略从 1 调整到 2,写入吞吐量能提升大约 30%,但这个改动务必要业务方确认好一致性容忍度再动。
3.2 100TB 级别下的备份恢复与归档策略
一旦数据规模上了 100TB,备份这个话题就不再只是"DBA 日常任务"了,它会直接决定整个运维体系的可靠程度。传统 MySQL 在 100TB 规模下的物理备份基本没法看了——xtrabackup 全量备份至少要跑十几个小时,备份文件占用的磁盘空间又是另一笔巨大开销。PolarDB 存算分离架构在备份上的优势,可以说是它最容易被低估的亮点。
因为数据实际存储在分布式存储池里,PolarDB 的备份是基于存储层的快照能力做的,可以在秒级生成一个一致性快照,完全不占用计算节点的 IO 资源。你可以一天做多次快照,成本极低,恢复时也可以选择任意一个快照时间点做快速恢复,把一个 100TB 的实例恢复到新集群,通常只需要几分钟到十几分钟,这个恢复速度在传统架构下是想都不敢想的。
这个特性落到实际操作上,最大的价值在于:你可以放心大胆地做频繁的沙箱验证和容灾演练,而不用像以前那样给每次演练预留一整天的窗口。我建议所有用了 PolarDB 的团队把月级容灾演练改成周级,每次演练直接从一个小时前的快照恢复出一个完整实例,然后让测试团队在上面跑核心链路回归。这套流程跑顺以后,整个团队的数据库运维安全感会提升一个档次。
归档策略方面,存算分离架构不一定需要你自己做冷热分离。PolarDB 的存储池支持按数据块的访问频度自动调整存储层级,冷数据会自动沉降到更低成本的存储介质上。你在业务层面唯一需要规划的,其实是生命周期明确的历史数据是否要迁出到独立实例。我见过不少客户把三年以上的订单数据留在原实例的归档库,用 PolarDB 的 DTS 工具做按时间范围的定期同步,这样既能保持主实例的轻量,又不影响历史数据查询,这个方案实操下来很成熟。
3.3 兼容性与迁移路径:从自建 MySQL 平滑迁移
PolarDB 的 MySQL 兼容性是我愿意向客户推荐它的一个重要原因。它不是"兼容大部分语法"这种程度,而是从协议层到 SQL 语义都做了深度兼容,这意味着大多数基于 MySQL 5.7 或 8.0 开发的应用,甚至不需要改一行代码就能跑在 PolarDB 上。当然,实际迁移时还是有几个细节值得提前确认。
第一个是数据库账号和权限体系的迁移。PolarDB 的账号体系跟 RDS MySQL 类似,但和自建 MySQL 的授权命令有一些差异,建议在迁移前做好账号清单整理,统一在目标端重建,不要尝试直接导入 mysql.user 表的数据,那是典型的自找麻烦。第二个是存储过程和定时任务的检查。如果业务里用了大量的存储过程和 Event Scheduler,迁移前需要逐个在 PolarDB 上做兼容性验证,因为个别内置函数的行为边界可能略有区别。第三个是参数配置的核对。自建 MySQL 的 my.cnf 里很多参数在 PolarDB 上是通过参数模板管理的,比如 innodb_buffer_pool_size 这类参数不再需要手动设置,而像 sql_mode、max_allowed_packet、timeout 这类参数要对照业务需求提前配置好。
迁移路径上,最稳的组合方案是先创建 PolarDB 实例,再用数据传输服务做全量迁移加增量同步。全量迁移的阶段可以放在业务低峰期执行,增量同步阶段观察主从延迟稳定在 0 到 1 秒之间以后,再选择一个维护窗口做最终的业务切换。我特别建议在正式切换前做至少两次全流程演练,第一次演练只验证数据完整性和主要功能链路,第二次演练就要带着故障预案做,把切换过程中可能出现的回滚场景也一并演练到位。很多团队第一次切流时手忙脚乱,基本都是因为演练次数不够,导致切换脚本里的隐藏问题没有提前暴露。
4. 常见问题与排查技巧实录
4.1 超大事务与锁等待问题
存算分离架构下的大事务,表现和传统 MySQL 有些相似,但排查的思路不太一样。我遇到过好几个客户反馈,业务高峰期 PolarDB 主节点的 CPU 使用率并不高,但 SQL 执行延迟很高,应用侧出现大量锁等待超时。这类问题十有八九来自超大事务。
因为 PolarDB 的存储是共享的,大事务持有行锁的时间一旦变长,所有访问同一行数据的其他事务都会被阻塞。更麻烦的是,在存算分离架构下,超大事务的提交过程需要等待 Redo Log 在存储池中完成多副本确认,所以事务越大,提交阶段的开销就越高。排查看两样东西:一是 information_schema.innodb_trx 表里有没有长时间未提交的事务,二是 Performance Schema 里的 events_statements_current 表,定位到具体是哪条 SQL 开了事务后长时间不提交。
实操层面,我一般会建议业务代码里统一加上事务超时控制,在应用层使用类似 Spring 的 @Transactional 注解时设置一个合理的 timeout 值,默认不要超过 30 秒。同时把事务内的查询尽量拆出去,只把写操作留在事务里,这种小的编码习惯可以在根源上减少大事务的产生。
4.2 存储IOPS与延迟抖动排查
存算分离架构下,计算节点和存储节点之间走的是网络,所以存储延迟不再是本地磁盘的微秒级,而是网络往返的毫秒级。这个架构差异需要团队提前建立好心理预期和监控基线。正常情况下,PolarDB 的存储池在百 TB 规模下,单次 IO 的 P99 延迟应该稳定在 1 到 3 毫秒之间。如果发现 IO 延迟出现持续抖动,首先要看是不是存储池的 IOPS 配额被打满。
排查方法很简单:在 PolarDB 控制台查看存储节点监控,关注"存储 IOPS 使用率"这个指标。如果持续超过 80%,就得考虑提升存储池的性能等级,或者把部分读流量从主节点迁移到只读节点。这里有个经验值,我在好几个客户现场验证过:当读流量占比超过 70% 时,把查询流量分流到只读节点,存储 IOPS 的总体压力能下降 50% 以上,延迟抖动问题通常会随之消失。
还有一种比较隐蔽的延迟抖动来自存储节点自身的块拆分和再平衡。当存储池容量使用率达到某个阈值后,系统会自动触发数据块的迁移和再平衡,这个过程会造成短时间的 IO 延迟升高。规避的办法是提前做好容量规划,不要等到 90% 再来扩容。我一般建议把存储使用率的安全水位控制在 70% 以内,这样既能留出足够的再平衡缓冲,也能避免在业务高峰期触发自动迁移。
4.3 连接数与连接池参数调优
存算分离架构带来的一个隐性变化是:因为只读节点扩展太方便了,很多团队会倾向于创建大量只读节点来分摊流量,结果每个节点都配置了很大的连接池,整体连接数一下子涨上去,直接把网络层和应用网关打懵了。这个问题我在两个客户现场都遇到过,排查起来其实不难,但需要提前预防。
PolarDB 集群的每个计算节点都有 max_connections 限制,不同规格对应的默认值不同。在 100TB 数据量下,主要写入节点建议保持默认配置即可,不要为了追求高并发而盲目调大 max_connections。因为连接数越大,上下文切换开销越大,反而会降低单节点的吞吐能力。更推荐的做法是控制应用连接池的最大连接数,配合只读节点的水平扩展来分摊压力。
这里分享一个我实际调过的参数组合。对一个 8 核 64GB 的主节点,应用连接池的最大连接数设置在 100 到 200 之间比较合适;只读节点如果是 4 核 16GB,最大连接数建议控制在 80 以内。然后根据业务读流量的增长,动态增加只读节点数量,而不是堆单节点的连接数。同时把 MySQL 侧的 wait_timeout 和 interactive_timeout 适当调短,比如从默认的 8 小时缩短到 1 小时,能有效避免应用侧异常退出时残留大量死连接。这套组合拳打下来,连接数相关的故障基本可以绝迹。
5. 写在最后:一次架构升级的真实体会
回头再看这三个客户,虽然行业不同、业务形态不同、数据特征也不同,但他们最后走到的位置是高度一致的——通过存算分离彻底告别了分库分表或者按年拆表这类反模式,把精力重新放回到业务本身。我个人在帮他们做方案选型和迁移评估的过程中,最大的体会是:PolarDB 存算分离的真正价值并不只是容量大,而是它把数据库最麻烦的存储扩展问题,从"基础设施改造"降维成了"控制台操作"。
如果你现在正对着一个 50TB 以上的 MySQL 实例发愁,或者刚被分库分表的改造量劝退,我的建议是不妨先拿一个非核心业务库到 PolarDB 上做一次完整的迁移演练。花半天时间验证一下兼容性和性能表现,远比你花几个月时间讨论架构方案要实在得多。数据量再大,也没有想象中那么可怕,关键是选对船。