一张表的两亿行时刻
故事从一张订单表说起。业务跑了两三年,订单表两亿行,平时查询有索引护体,看起来岁月静好。直到产品要在订单上加一个「渠道来源」字段——DBA 拿着方案皱了半天眉:在线 DDL 工具全量拷表加回放增量,八小时起步;拷表期间磁盘要多占一份空间,主从延迟会全程飘红。最后 DBA 说了一句让人清醒的话:这不是慢,是这张表已经不配再被改了。
单表规模逼近天花板时,问题从来不是一个,是一串:索引膨胀——B+ 树层级涨上去,每个查询多几次磁盘 IO;DDL 慢如老牛——任何结构变更都要全量拷表;备份窗口失控——全量备份跑不完一个夜里;磁盘与内存水位——Buffer Pool 装不下热数据,命中率往下掉。这一串问题互为帮凶,拖到最后就是线上事故。
什么规模才算到顶
先泼冷水:两亿行不等于必须拆。行数只是表象,真正的判断依据是「顶格优化之后还撑不撑得住」。行业里常见的参考线是:单表行数向千万级靠拢、单库数据量向 TB 级靠拢、QPS 向单实例瓶颈(万级上下)靠拢——三条里撞了两条,就该认真评估。但参考线只是提醒,不是判决,很多所谓「必须拆」的场景,把索引建对、把冷数据归档掉、把读流量分出去之后,还能再撑两年。
所以这个系列的第一课是克制:分库分表是高成本的重构,不是性能优化的第一步。它带来的复杂度——跨片查询、分布式事务、数据迁移、运维翻倍——会跟随系统余生。下一篇先把「拆之前还能做的三招」讲透,三招打完还顶不住,再来动刀。
拆库与拆表:两把不同的刀
「分库分表」四个字其实混着两种手术。分表:把一张大表切成多张结构相同的小表,可以只在一个实例里分(table_0 到 table_63),也可以分到多个实例。分表直接解决单表问题——索引层级、DDL 时长、行数规模;但如果这些表还住在同一个实例里,实例级的瓶颈(CPU、内存、磁盘、连接数、QPS 上限)一个都没解决。
分库:把数据拆到多个 MySQL 实例上,每个实例独享 CPU、内存和磁盘,写能力与容量一起线性扩展。分库必然分表(每个实例上就是一张张小表),而分表不一定分库。所以真正的选型问题只有一个:瓶颈在表还是在实例?表大就分表,实例撑不住就分库,多数大系统最后两者都要——「N 库 M 表」的矩阵式拆法就是答案,比如 8 库 64 表,每个库 8 张。
动刀前的全景清单
确定要拆之后,决策链有五环,这个系列按这个顺序展开:怎么切——垂直还是水平,刀口选在哪(第 3-4 篇);键怎么定——分片键是终身选择(第 5 篇);路由怎么落——代码、代理还是 SDK(第 6-8 篇);查询怎么办——跨片聚合、分页、JOIN(第 9-10 篇);数据怎么迁——双写、校验、切流(第 14-15 篇)。每一步都有回头路,唯独分片键没有——它会锁死系统的查询能力很多年。
下一篇,先把刀放下,聊聊拆之前那三招顶格优化——很可能你的两亿行,瘦完身就不需要这一刀了。
评论 (0)