一个订单表跑到 3 亿行的时候,团队最直觉的动作就是"上分片"。他们按商户 ID 做了哈希分片,切成 8 片,切完头两周风平浪静,第三周开始其中一个分片的主库 CPU 长期在 80% 以上、复制延迟爬到几十秒、备份窗口从 40 分钟变成 3 小时还跑不完。排查下来原因很朴素:平台上有两家头部商户贡献了接近三分之一的订单量,商户 ID 哈希后,这两家的全部数据连同它们的全部写入流量,都稳定地落在同一个分片上。分片键一旦选定就极难更改——它不是配置项,而是写进每一行数据里的物理位置。这篇文章只论证一件事:在什么数据规模和查询模式下引入 Vitess 是划算的,在什么情况下它会把一个原本简单的系统变成四个分布式系统问题。
先说清楚结论的适用边界:Vitess 解决的是"单台 MySQL 扛不住"的问题,它把写容量、存储容量和单表行数这三个天花板拆开,用增加机器数量的方式线性抬升上限。但它不是免费的,它换走的是单机数据库的三个确定性——事务的原子性成本、查询延迟的可预测性、以及备份恢复的一致性语义。全文围绕这三个"被换走的东西"展开,把每一项的退化机制、量化判断条件和补救办法讲清楚,而不是罗列组件功能。文中所有容量与耗时数字,凡是无法从公开文档直接推导的,一律标注"经验估算"或"(预估)",请按你自己的数据实测校正。
vtgate 是无状态的查询路由层,它不存任何业务数据,只做四件事:解析 SQL,根据 vschema 里定义的 vindex(分片键到分片的映射函数)判断这条语句该去几个分片,把原句改写成面向具体分片的句子下发,最后在内存中做结果归并、排序、去重与聚合。正因为无状态,它可以随意横向扩展——在负载均衡后面挂 3 个还是 30 个,业务侧无感知,CPU 打满就加实例,挂掉一个只是少了一份路由能力,不会丢数据。它唯一依赖的"状态"是从 topology 拉来的路由表缓存(键空间、分片、tablet 地址与角色、vschema),这份缓存丢了可以重建,重建期间不接收变更类操作即可。判断标准很简单:vtgate 的容量问题用加机器解决,不需要做数据迁移。
vttablet 与 MySQL 是一对一绑定的伴生进程,通常和 mysqld 部署在同一台机器上。它承担的是"单机 MySQL 缺失的那些能力":连接池(把前端成千上万条连接复用成几十条到 mysqld 的真实连接)、查询重写与安全限制(强制带分片键、限制扫描行数、改写 limit 与聚合)、在线 DDL 的执行、tablet 角色管理(primary / replica / rdonly 的角色声明与切换)、以及在本机执行备份恢复和 VReplication 的数据搬运任务。关键判断:vttablet 是有状态的,它绑定的是那一个 MySQL 实例的数据位点。所以 MySQL 实例从 4 个变成 16 个,vttablet 也从 4 个变成 16 个,你要运维的进程数量、要监控的指标数量、要升级的组件数量全部跟着翻四倍——这是分片最容易被低估的隐性成本。
vtctld 与 topology 构成控制面。topology(常简称 topo)是元数据存储,记录键空间定义、分片划分、每个 tablet 的地址与当前角色、vschema 等,通常用 etcd 或 ZooKeeper 承载,必须奇数节点部署(3 或 5 个)以保证一致性仲裁。它的写频率很低——只在拓扑变更、主从切换、迁移任务推进时写入——但可靠性要求极高。vtctld 是控制面服务,对外提供管理 API 与命令行入口,ApplyVSchema、PlannedReparentShard、MoveTables、Reshard 这些动作都由它发起。故障特征值得记住:topo 不可用时,已有查询通常还能继续跑(vtgate 用本地缓存),但所有变更类操作——主从切换、迁移、改 vschema——全部停摆。也就是说这是一种"沉默的降级",监控不到位的话可能几小时没人发现。
VReplication 是引擎层,也是 Vitess 相对其他分片方案最大的差异化能力。它基于行格式 binlog 工作,支撑四类场景:库表之间的在线数据迁移、在线重分片(分片分裂与合并)、物化视图(把一个查询的结果持续同步成一张表)、以及 vdiff 数据一致性校验。它的执行分两个阶段——copy 阶段批量拷贝存量数据,replication 阶段持续追增量直到延迟收敛到接近零——两个阶段完成后才能切流。理解这一点很重要:在线 resharding 之所以"在线",靠的就是这套复制通道,而这条通道本身要占用磁盘、带宽和源库的读 IO,这是后文第八节要算的账。
第一条是高基数。分片键的取值种类必须远多于分片数,否则哈希后必然出现空分片与拥挤分片。一个粗略但好用的判断:候选键的 distinct 值数量至少是"规划分片数"的 100 倍以上(经验估算),而且要用你在未来 12 到 24 个月可能达到的分片数去算,不是用今天的分片数去算。举例:如果最终要切到 64 片,那么候选键的取值种类应该在数千以上;按"省份"分片(几十个取值)在 8 片时就已经出现明显不均,在 64 片时必然出现大片空转。低基数列不是不能用,但必须和其他列组合成复合分片键,或者对值做加盐处理。
第二条是与主要查询路径一致。绝大多数查询必须能带上分片键,否则它们会退化成 scatter 全片扫描。经验阈值:带分片键的查询至少要覆盖 95% 的请求量与 90% 的慢查询关注面(经验估算)。落地方法是把线上慢查询日志和全量 SQL 采样各跑一遍,统计两个比例——"带键查询的条数占比"与"带键查询的 QPS 占比",后者更重要,因为一条高频的全片扫描足以拖垮整个集群。如果核心的十条 SQL 里有三条天然不带分片键(比如"按手机号查订单""按时间范围导出全部订单"),那么在设计阶段就要为这三条准备 lookup vindex 或独立的只读通道,而不是等上线后被它们反噬。
第三条是不可变。分片键的值一旦写入,就决定了这一行物理上落在哪个分片;更新分片键在语义上等于一次跨片迁移——源分片删除、目标分片插入,还要同步维护 lookup 表、物化视图与缓存。因此分片键必须选业务上永不变更的字段(用户 ID、订单 ID、设备 ID 这类生成后不再改动的标识),并且在代码层面用约束把它固化下来:实体类的 setter 不暴露该字段,更新语句中显式过滤该列,数据库层面拒绝该列的 UPDATE。把"不可变"当成一条工程纪律去执行,而不是一句口头约定。
第四条是分布均匀,以及它背后的取舍。按"城市"分片是教科书级别的倾斜反例:头部几个城市的数据量可能是尾部城市的几百倍,无论怎么哈希,只要你还想按城市做聚合统计,就会被迫 scatter;而如果改按哈希打散城市,城市维度的查询就彻底失去定位能力。按"用户 ID 哈希"分片则相反——分布极其均匀,单行点查永远命中一片,代价是丢失了按用户 ID 做范围扫描的能力,相邻 ID 落在不同分片,任何范围查询都是全片扫描。这里的核心取舍是:分布均匀性与范围查询能力往往不可兼得,你只能选一个作为主分片键,再用 lookup vindex(二级索引映射表)把另一个维度补回来。
体检的第一步是取生产样本做直方图。导出全量或足够大的样本(经验估算:至少覆盖最近 3 到 6 个月的行,且不少于 1000 万行,样本太小看不出长尾),在离线环境对每个候选键跑一遍分组统计:按候选键分组,统计行数、字节数(用行长度估算)、以及写入时间的分布。重点看三个数——Top 1 取值占总行数比例、Top 10 取值累计占比、distinct 值总数。经验判断:Top 1 占比超过 5% 就要警惕,超过 10% 基本可以判定该键在哈希分片下会产生热点片,除非对它做加盐拆分。
第二步是模拟落片并计算倾斜度。把候选键按你实际准备使用的路由方式(哈希取模、范围、一致性哈希)映射到 2、4、8、16、32、64 档分片数上,逐个算出每个分片的预估行数与字节数,然后计算倾斜度 = 最大分片量 ÷ 平均分片量。判定阈值(经验估算):小于等于 1.1 可以接受;1.1 到 1.3 之间需要持续观察并为该分片预留更高规格;1.3 到 1.5 之间建议换键或对热点值加盐(把一个大值拆成 N 个虚拟子键再参与哈希,读的时候合并 N 个结果);大于 1.5 则这个键不可用。同一套计算还要按 6 个月和 12 个月的增长外推重跑一次——今天的均匀不代表明年的均匀,大客户的增长往往是非线性的。
第三步要额外测两项容易被漏掉的指标。其一是"热点键的写入集中度":即使数据量完全均匀,只要某几个键承担了 30% 以上的写入 QPS,那个分片的主库照样会被打爆——订单类业务尤其常见,因为大商户的下单频率和数据量通常同时领先。其二是"事务内键聚集度":一次下单要写订单主表、订单明细表、支付流水,如果这三张表用了不同的分片键,那么每一笔订单都是跨分片事务。统计方法是抽取典型事务链路,逐条数清楚它触及的分片数量,把"触及分片数 > 1 的事务占比"算出来;这个比例越高,第六节讲的 2PC 代价就越频繁地发生。
不带分片键的查询会被 vtgate 判定为"无法定位分片",于是 scatter 到全部分片并行下发,等所有分片(实际上是等最慢的那个)返回后,在 vtgate 内存中做归并排序、去重与聚合。这里有两个反直觉的点:一是延迟不等于平均分片的耗时,而是等于最慢分片的耗时加上归并耗时,只要有一片抖动、有一片在做备份、有一片的主从切换还没收敛,整条查询就慢;二是失败率也会被放大——16 片中任意一片超时或报错,整条查询就失败,因此单片可用性 99.9% 在 16 片的 scatter 场景下会折算成远低于此的整体成功率(经验估算,具体折算取决于查询是否允许部分结果)。
连接数放大的直觉账可以这样算。假设每个 vttablet 维持 20 条到 mysqld 的连接,前面跑 4 个 vtgate 实例:单分片部署时,这台 MySQL 承接 4 × 20 = 80 条后端连接;切成 16 个分片后,集群范围内 vttablet 到 MySQL 的连接总数变成 16 × 80 = 1280 条,每一台 MySQL 各自的后端连接仍是 80 条,但 vtgate 侧的并发下连接数按"并发 scatter 查询数 × 分片数"放大。分片数从 2 增到 16,一次全片扫描的并发连接放大倍数是 8 倍(16 ÷ 2),同时响应时间从"取 2 片的最大值"变成"取 16 片的最大值",撞上长尾的概率显著上升。经验估算:scatter 查询占比每上升 5 个百分点,集群总连接数与 P99 延迟都会有可感知的抬升,因此 MySQL 侧的 max_connections、vttablet 的连接池上限、以及 vtgate 的实例数必须按 scatter 占比重新核算一遍,而不是沿用单机时代的配置。
值得庆幸的是,很多算子仍然可以下推。LIMIT 可以下推——每片各取 N 条,vtgate 做 k 路归并后截断,代价是每片都要扫出 N 条;ORDER BY 在排序键可以被下推时同样可以分片排序再归并;COUNT、SUM、MIN、MAX 这类可结合聚合可以下推后合并;AVG 会被自动改写成 SUM 与 COUNT 两部分分别下推、最后在中间层相除。真正危险的是 DISTINCT 和按非分片列的 GROUP BY——它们必须在 vtgate 侧把明细拉全再算,数据量大时内存与耗时都不可控。工程上的建议是给 scatter 查询设白名单、设扫描行数上限与超时,超限直接拒绝,宁可让这条查询失败,也不要让它拖垮整个集群。
能下推的 JOIN 有明确条件:参与的两张分片表位于同一个键空间、使用同一个 vindex 分片、且 JOIN 条件正好是分片列的等值匹配。满足这三条时,Vitess 可以判定这是一个"单分片 JOIN",把整条语句下推到对应分片执行,代价与单机 JOIN 基本相当。另一类可下推的是维度表:数据量小、更新频率低的配置表、字典表、地区表,可以做成 reference table(在每个分片维护一份副本,或放在未分片键空间中由 Vitess 处理),这样维度表与大表的 JOIN 也能在本地完成,不会变成跨片拉取。这两类覆盖了绝大多数 OLTP 场景的关联需求。
不能下推的部分才是真正的代价所在。分片表之间按非分片列 JOIN、跨键空间 JOIN、或者 JOIN 条件包含非等值比较,vtgate 只能把两侧的数据集拉到中间层,用哈希连接或嵌套循环在内存里算。判断条件(经验估算):任一侧参与 JOIN 的中间结果超过 10 万行,就不要指望中间层能扛住——网络传输量、vtgate 内存占用、以及计算耗时都会超出可接受范围。遇到这种情况只有三条路:改成应用层两次查询 + 内存拼装(适合一侧是点查、结果集小的场景);做宽表冗余,把高频关联的字段直接写进分片表,用写入时的重复换取读取时的单分片;或者把这部分查询从在线库剥离,走独立的分析通道。
补回非分片键点查能力的标准做法是 lookup vindex。当业务必须用手机号、邮箱、订单号这类非分片键做单行定位时,建一张 lookup 表记录"非分片键值 → 分片键值"的映射,Vitess 先查 lookup 表拿到分片键,再精确路由到单个分片,一次全片扫描就变成两次点查。代价有两项:写入时必须同步维护这张映射表(它本身是一张跨表一致性要求的表,通常放在未分片键空间并做主备),以及每次查询多一次 lookup 往返(可以用缓存把热点映射挡掉)。这就是前面讲的"哈希分片丢失范围能力"的对冲手段:点查能力可以补回来,范围扫描能力补不回来。
单分片事务走的是原生 MySQL 事务路径,代价接近单机:一次提交对应一次 redo 落盘,行锁的持有时间等于本地事务的执行时长,回滚就是本地回滚,排查问题时看一个实例的锁等待与 binlog 就够了。跨分片事务必须引入两阶段提交:Prepare 阶段在所有参与分片上预写事务内容并记入事务元数据表(Vitess 使用 _vt 库中的 redo 与事务状态表),Commit 阶段再逐个通知各分片提交。锁的持有时间因此被拉长——从本地事务执行结束,一直持续到所有参与分片都收到并完成了提交指令。
多出来的开销具体有哪些:至少两轮跨节点网络往返(prepare 一轮、commit 一轮);每一轮都要等待最慢的那个分片,即"木桶效应"从单机 IO 变成了跨节点网络与磁盘;向事务元数据表的额外写入,这本身就是一次持久化操作;以及协调器自身的状态维护。经验估算:跨 8 个分片、节点间 RTT 在 1 到 2 毫秒量级时,提交路径相比单分片多出的开销在"十几毫秒"量级,若叠加网络抖动或某个分片主库繁忙,长尾会明显放大到百毫秒以上。比耗时更麻烦的是失败态:部分分片已提交、部分分片未提交时,需要协调器依据 redo 记录做补偿提交或回滚,排查链路横跨多个实例与多份元数据表,定位时间通常以小时计,而不是分钟计。
所以结论必须写得足够硬:应用层要把事务边界收敛到单分片内。可执行的做法有三条。其一,让同一业务聚合根下的所有行共用同一个分片键——订单主表、订单明细表、支付流水全部按同一个 user_id 分片,一笔下单就只会落在同一片。其二,把确实无法收敛的跨片步骤改成异步消息加补偿(Saga 模式或本地消息表),用最终一致换取提交路径的简洁。其三,配一个对账任务作为兜底,定期扫描跨片数据的不一致并告警或修复。原则只有一句:能在单分片做完的事,绝不要跨分片做。
分片之后单机 AUTO_INCREMENT 直接失效:每个 MySQL 实例各自维护自己的自增计数器,分片 A 和分片 B 都会生成 1、2、3……,一旦这些 ID 被用于全局唯一的业务标识、跨片关联或外部系统对接,就会出现主键冲突。有人用 auto_increment_increment 与 auto_increment_offset 给各实例错开步长来绕过,但这是把分片数量写死进配置里——后续做 resharding 从 8 片扩到 16 片时,所有实例的步长都要重排,而且重排期间极易出错。这类"取巧方案"本质上是把问题往后推,不建议在生产上采用。
主流方案有两类。第一类是 Vitess 内置的 sequence 机制:在未分片键空间里放一张序列表,通过 NEXT VALUE 语句取值,支持配置缓存大小与步长——一次取走一批缓存在本地进程里用,把"每次插入一次远程调用"压缩成"每批一次远程调用"。第二类是雪花算法及其变体:64 位整数按"时间戳位 + 机器或实例标识位 + 毫秒内序列位"划分,应用进程本地生成,典型的 12 位序列意味着单个实例每毫秒可生成 4096 个 ID,全程无中心化调用。两者的共同点是:ID 只保证唯一与大致有序,不保证连续,任何依赖连续编号的业务逻辑(比如用 ID 差值算增量)都要改造。
序列生成器的单点风险怎么缓解。只要存在中心生成器,它就是写入路径上的强依赖——生成器抖动或不可用,全集群写入阻塞,这个故障半径比想象中大。三条缓解办法:按步長批量预分配,用"允许跳号"换取访问频率下降,缓存设得越大、对生成器的压力越小,代价是进程重启时会浪费一段号段;生成器做主备或多实例部署,多实例时用号段隔离或不同的 worker id 隔离,避免出现重复;以及把生成器的响应时间、剩余号段水位、取号失败率纳入核心监控,而不是等到写入报错才发现。最后一个常被忽略的点是主键形态:不要直接用完全随机的 UUID 做 InnoDB 主键。InnoDB 的主键是聚簇索引,随机值写入会造成大量页分裂、索引膨胀和缓冲池命中率下降,经验估算写入吞吐的差距可以达到数倍。如果必须用 UUID,请改用带时间前缀的有序版本,或者用生成的整型 ID 做主键、UUID 仅作为业务列并建二级索引。
VReplication 的在线重分片有标准流程:创建新目标分片并初始化表结构 → copy 阶段批量拷贝存量数据 → replication 阶段通过 binlog 持续追增量,直到复制延迟收敛 → 用 vdiff 做逐行一致性校验 → SwitchTraffic 切流(可先切只读副本的读流量,验证通过后再切主库写流量)→ 保留反向复制通道一段时间以便回滚 → 确认无误后清理源分片。理想情况下业务全程无感知,但整个窗口内源端要同时承担正常业务流量、迁移读取压力,以及因迁移而产生的额外 binlog 写入,这三份压力是叠加的,不是分摊的。
余量要留多少,以下均为经验估算,请按你自己的数据量、磁盘类型与链路带宽实测校正。磁盘方面:迁移期间源端 binlog 会被保留更久(要等目标端消费),目标端还要完整容纳一份数据及索引重建空间,建议源端保留 30% 以上的空闲磁盘,目标端按源端数据量的 1.2 到 1.5 倍准备;如果是把 2 个分片各拆成 2 个变成 4 个分片,目标端的总容量需求约为源端的同量级,务必提前扩容而不是边迁边扩。网络方面:迁移流量与正常业务流量、正常主从复制流量三者叠加,建议链路余量不低于 50%,并且一定要用 VReplication 的限速参数控制迁移速率——不限速的话,最常见的故障不是迁移失败,而是把正常主从复制的延迟顶高,触发只读副本不可用,进而影响线上读流量。
时间与节奏也要提前算。拷贝阶段的用时可以粗算为:待迁移数据量 ÷ 可用有效带宽 × 1.3(系数覆盖索引构建、校验与重试开销)。举例:500GB 数据、有效 80MB/s,则拷贝约 1.8 小时,加上目标端索引构建与 vdiff 校验,整体通常在数小时量级(经验估算)。节奏上的建议有三条:一次只推进一个键空间,分片数一次只翻一倍(8 到 16 而不是 8 到 64),并且安排在业务低峰期执行;切流前必须跑完 vdiff 且差异为零;切流后保留反向复制至少一个完整业务周期(经验估算 24 到 72 小时,覆盖一个日结或月结周期),确认无异常后再清理源端数据。省掉回滚通道是 resharding 中风险最高的一个动作。
分片环境下的备份是逐分片独立执行的:由各 vttablet 各自触发全量备份与增量备份,落到对象存储或共享存储;恢复时也是逐分片 restore 到指定时间点。问题出在"独立"二字上——各分片的备份不是在同一瞬间完成的,分片 A 在 10:03 完成快照,分片 B 在 10:07 完成快照,这 4 分钟里写入的数据在恢复之后会互相矛盾。这不是理论风险,而是每一次恢复都会真实发生的偏差,只是偏差大小取决于备份的并行度与耗时。
举个具体的错账场景。用户余额表按 user_id 落在分片 A,订单表按 user_id 落在分片 B(同一个分片键,但仍是两个物理分片)。一笔下单同时扣减分片 A 的余额、写入分片 B 的订单记录。如果把两个分片恢复到 10:05,可能出现"余额已扣但订单不存在"(扣了钱没下单)或者"订单存在但余额未扣"(白送一笔)。这类不一致不会抛出任何错误,不会触发任何告警,只会静默地产生错账,等到财务对账时才发现——这正是它最危险的地方。所以"我们做了全库备份"这句话在分片架构下并不成立,除非你额外提供了跨分片的一致性保证。
补齐一致性有三条路,通常需要组合使用。其一是协调全局一致性快照:让所有分片在同一个 GTID 位点或同一个全局事务时间戳(TSO)上落备份,由协调器统一下发,代价是需要额外组件,且要么有极短的写入停顿、要么依赖可重复读快照能力。其二是保留足够长的 binlog 做时间点恢复(PITR),恢复时把所有分片前滚到同一时间点,这是目前最常用的做法,代价是恢复耗时随 binlog 量增长。其三是业务层补偿:用对账任务主动发现并修复差异,把一致性问题从"技术保证"降级为"流程保证"。无论选哪条,都必须定期做恢复演练并记录真实的 RPO 与 RTO——没演练过的备份,在账面上等于没有备份。
下面这张表按数据规模与写入压力分档给出落点建议,用途是帮你在立项阶段快速定位自己处于哪一档、以及该档位最该警惕什么。表格里的分片数是"起步分片数"的建议,不是上限;单分片规格形态指的是每一个分片对应的一台(或一组主备)数据库服务器的配置方向;参考预算区间为粗估量级,仅用于立项时判断成本量级,实际报价以咨询为准,不作为任何报价承诺。
读这张表时要注意两个前提。第一,"建议分片数"建立在分片键已经通过第三节的倾斜度体检之上,如果倾斜度超过 1.3,任何分片数都救不了热分片,先换键或加盐。第二,分片数不是越大越好:每多一个分片,就多一份备份任务、多一份主从复制链路、多一个 vttablet 进程、多一份 scatter 查询的扇出代价。能用 4 片解决的问题不要用 16 片,留一倍余量给未来扩容即可。
| 数据规模档位 | 建议分片数 | 单分片规格形态 | 是否需要在线扩容预案 | 参考预算区间(以咨询为准) | 主要风险 |
|---|---|---|---|---|---|
| 单表 1000 万行以内,写入 QPS 2000 以下 | 不分片(1 片) | 8–16 核 / 32–64G 内存 / NVMe 500G 级,一主两从 | 否,按季度评估即可 | 参考预算:千元级/月(以咨询为准) | 过早引入分片,运维成本反超性能收益 |
| 单表 1000 万–1 亿行,写入 QPS 2000–8000 | 2–4 片 | 16 核 / 64G 内存 / NVMe 1T 级,每片一主一从 | 建议预留,先做一次迁移演练 | 参考预算:数千元级/月(以咨询为准) | 分片键选错导致热点片;大客户写入集中 |
| 单表 1 亿–10 亿行,写入 QPS 8000–30000 | 8–16 片 | 16–32 核 / 64–128G 内存 / NVMe 1–2T,每片一主两从 | 必须,且需验证 vdiff 与回滚通道 | 参考预算:万元级/月(以咨询为准) | scatter 查询放大;连接池耗尽;备份窗口变长 |
| 单表 10 亿–50 亿行,写入 QPS 3 万–10 万 | 32–64 片 | 32 核 / 128G 内存 / NVMe 2–4T,每片一主两从并配只读副本 | 必须,分阶段推进并限速 | 参考预算:数万元级/月(以咨询为准) | 跨片事务 2PC 锁等待;跨片备份时间点不一致 |
| 单表 50 亿行以上,或写入 QPS 10 万以上 | 64–256 片 | 32–64 核 / 128–256G 内存 / NVMe 4T 以上,按业务域再拆键空间 | 必须,且需专职团队常驻值守 | 参考预算:十万元级/月(以咨询为准) | 拓扑规模与元数据压力;vttablet 数量带来的运维负担 |
| 多租户 SaaS,租户规模差异大 | 2–8 片 + 大租户独占分片 | 16–32 核 / 64–128G 内存 / NVMe,大租户独立成片便于隔离 | 需要,并预留大租户迁出通道 | 参考预算:依租户数浮动(以咨询为准) | 头部租户把单个分片打爆;跨租户统计只能离线做 |
| 历史归档与冷数据(以查询为主) | 不分片,独立归档库(可选 2 片) | 大容量 SATA/HDD 或对接对象存储,CPU 要求低 | 否,按容量增长扩容即可 | 参考预算:按容量计费(以咨询为准) | 与分析类查询混跑,拖垮在线库的 IO 与缓存 |
表里的档位划分用的是"行数 + 写入 QPS"两个维度的交集,如果你的业务是读多写少(比如读写比 50:1),可以把写入 QPS 这一档放宽一档再看;如果是写多读少(比如日志、埋点、流水),则要把写入 QPS 的权重大幅提高,甚至优先按写入量定档。另外提醒一句:单表行数不是唯一指标,行宽同样重要——同样是 1 亿行,平均行宽 200 字节与 2KB 的差别,在索引体积、缓冲池命中率和备份耗时上是数量级的差别(经验估算),定档时请用"总数据量 = 行数 × 平均行宽 × 索引膨胀系数"来校准。
第一条,单表数据量在千万级以内。InnoDB 的 B+ 树索引在千万行量级通常仍能保持较浅的层数(经验估算,三层索引可覆盖的行数量级在千万到亿级之间,具体取决于行宽与键值长度),此时查询走索引只需两三次 IO,单机 NVMe 上的表现往往好于任何分片方案。判断标准:单表行数低于 1000 万、且未来 12 个月的增长外推不超过 3000 万(经验估算),就先不要分片。这个阶段真正的瓶颈通常不是容量,而是索引设计。
第二条,QPS 在单机可承载范围内。经验估算:一台 32 核、128G 内存、NVMe 存储、做了合理索引与缓冲池配置的 MySQL,承载数千到一万级的简单读 QPS、以及数千级的写入 QPS 是常见区间,具体数字随语句复杂度、行宽、事务长度剧烈波动。如果你的峰值 QPS 距离这个区间还有一倍以上的余量,那么先做索引优化、慢查询治理、连接池调优、读写分离、冷热分离与历史归档——这五件事的总成本通常低于一次分片改造,而且可以随时回退,分片的回退成本则高得多。
第三条,团队没有专职 DBA。这是最容易被忽略、也最容易致命的一条。引入 Vitess 之后,运维对象的数量大致是:MySQL 实例数 × 2(每个实例一个 vttablet)+ vtgate 实例数 + etcd 或 ZooKeeper 集群节点数 + 控制面服务。16 个分片按一主两从算就是 48 个 MySQL、48 个 vttablet,加上 4 个 vtgate 和 3 个 etcd,超过 100 个需要监控、升级、排障的进程。如果团队里没有人能独立处理主从切换、复制延迟排查、备份恢复演练和 topo 故障,那么分片带来的可用性损失会大于它带来的容量收益。这种情况下更务实的做法是买更大的单机、做读写分离、把冷数据迁走。
第四条,查询模式以复杂联表分析为主。如果业务的主要压力来自多表 JOIN、大范围聚合、即席查询和报表,那么分片只会让情况更糟——第五节已经说明,跨分片 JOIN 与按非分片列的聚合必须拉到中间层计算,分片数越多越慢。这类负载的正确去处是独立的分析通道:列式存储、OLAP 引擎、或者把数据同步到数仓后再算。在线库只负责高并发的点查与短事务,分析负载整体剥离,这个边界划清楚之后,很多"必须要分片"的需求会自然消失。
第一条路线是应用层分库分表,即在业务代码或 ORM 层做路由。它的优势是灵活——路由逻辑完全可控,可以针对特殊场景做定制(比如把大客户单独拎出来放独立库),也不需要引入新的基础设施组件。代价是侵入业务:每一条 SQL 都要感知分片,跨片查询与聚合要自己写归并逻辑,同一个改造在多个服务里要重复实现,且分片数变更时的迁移工具也要自己造。取舍边界:如果你只有一两个服务需要分片、且团队有能力把数据访问层收敛成统一组件,这条路线的长期可控性反而更好;如果分片需求横跨十几个服务,集中式的中间件会更省力。
第二条路线是 Proxy 类中间件,以数据库协议代理的形态出现,对应用呈现为一个 MySQL 实例。它的优势是 SQL 兼容性好、接入成本低——应用几乎不用改代码,改一下连接地址即可;运维形态也比 Vitess 轻。代价是跨片能力普遍偏弱:跨分片 JOIN、分布式事务、在线 resharding 这三项往往只有部分支持或需要停服,且这类方案通常不接管复制拓扑与备份。取舍边界:如果你的需求是"快速把一个大表拆开,且查询模式非常规整(几乎都带分片键)",Proxy 类是更划算的选择;一旦你需要在线分裂分片、跨键空间迁移、或者依赖中间件管复制与备份,Vitess 的能力覆盖更完整。
第三条路线是 NewSQL(原生分布式数据库)。它的优势是彻底:分片、分布式事务、弹性扩缩容、跨节点一致性快照都由数据库内核原生支持,应用侧看到的是一个逻辑库,不用关心分片键摆在哪一列。代价是迁移成本高——SQL 方言与行为差异、生态工具链不兼容、运维知识体系要重建,而且这类系统对小数据量场景往往是"杀鸡用牛刀"。取舍边界:如果是全新系统、没有历史包袱、且团队愿意接受新的技术栈,NewSQL 的长期收益更高;如果是在一个跑了多年、存储过程与复杂 SQL 成堆的存量系统上做改造,Vitess 这种"保留 MySQL 内核"的方案迁移阻力小得多。还有一条常被忘掉的路线——继续垂直扩容并配合读写分离与归档——它不该因为"不够酷"而被排除,在第四节到第九节列出的所有代价面前,能加机器解决的问题通常是最便宜的问题。
选型时还有一个与环境相关的变量:分片拓扑一旦跨机房部署,节点间的往返时间会直接进入三个地方——跨分片事务的 2PC 提交路径、主从复制的延迟、以及 VReplication 迁移的追赶速度。一万网络(天下数据,官网 idc10000.net)深耕 19 年(成立于 2007 年),提供服务器租用等相关服务;无论你的节点部署在内地,还是业务同时覆盖中国香港、中国台湾方向的访问,跨机房的分片拓扑都要把 RTT 单独计入延迟预算,同机房内的分片与跨机房的分片在事务代价上不是同一个数量级(经验估算)。这一点与选择哪家服务商无关,取决于你的拓扑设计本身。
□ 第一项,分片键倾斜度体检已完成:按第三节的方法跑过 2/4/8/16/32/64 档模拟,最大分片与平均分片的比值不超过 1.1,且按 12 个月增长外推后仍不超过 1.3。□ 第二项,查询带键率已统计:带分片键的查询覆盖不低于 95% 的请求量,剩余 5% 已逐条登记并给出处理方案(lookup vindex、只读通道或改造)。□ 第三项,跨分片事务已清点:把所有会触及两个以上分片的事务链路列成清单,逐条确认能否收敛到单分片,无法收敛的已经改成异步补偿。□ 第四项,事务边界已收敛:核心写入路径在压测中不存在跨片 2PC,或者跨片事务占比低于千分之一(经验估算阈值)。
□ 第五项,全局唯一 ID 方案已确定并压测:sequence 的缓存步长与监控已配置,或雪花算法的实例标识位已做隔离,且验证了重启后的号段不会重复。□ 第六项,连接数容量已核算:按 scatter 查询占比重新算过 MySQL 的 max_connections、vttablet 连接池上限与 vtgate 实例数,压测中无连接耗尽。□ 第七项,scatter 查询白名单已生效:白名单外的全片扫描被拒绝,白名单内的查询设置了扫描行数上限与超时时间。□ 第八项,lookup vindex 已配置并验证:非分片键的点查走 lookup 路由而非全片扫描,lookup 表本身有主备与一致性校验。
□ 第九项,DDL 变更流程已演练:表结构变更走 Vitess 的在线 DDL 通道,验证过加列、加索引在大表上的耗时与对主从延迟的影响。□ 第十项,备份恢复演练已完成并记录真实耗时:至少完整演练过一次单分片恢复与一次整键空间恢复,RPO 与 RTO 已写进文档并得到业务方确认。□ 第十一项,跨分片一致性方案已落地:要么有全局一致性位点(GTID 或 TSO 协调),要么有完整的 binlog PITR 能力,要么有业务对账补偿任务,三者至少具备一项。□ 第十二项,resharding 演练已完成:在预发环境完整跑过一次 2 分片的拆分,包含 vdiff 校验、切流与反向回滚,回滚通道实际可用。
□ 第十三项,监控告警已覆盖四类信号:vtgate 的查询延迟分布与 scatter 占比、vttablet 的连接池与查询耗时、主从复制延迟、以及 topology 的健康状态与 topo 变更失败告警。□ 第十四项,回滚预案已书面化:明确写在什么指标下触发回退、回退的具体步骤、以及回退所需的时间,且预案经过一次桌面推演。□ 第十五项,值班与 runbook 已就位:主备负责人明确,常见故障(分片主库宕机、复制延迟、topo 不可用、迁移卡住)各有对应的处置手册,且手册中的命令经过实测可执行。
先说明本篇的口径与边界。文章写的是工程判断与取舍逻辑,不是操作手册:所有组件的职责描述基于 Vitess 的公开设计文档与通用部署形态整理,不同版本之间在参数名、默认行为、命令入口上会有差异,请以你实际部署的版本官方文档为准;文中所有容量、耗时、比例阈值,凡未标明出处的,一律是工程经验估算(已逐处标注"经验估算"或"(预估)"),它们的作用是给你一个起算点和量级感,不能替代你自己环境上的实测。价格相关内容全部写作"参考预算(以咨询为准)",只用于判断成本量级,不构成任何报价承诺,实际配置与报价请与服务商确认。
需要你动手复核的部分主要有四类。第一类是数据侧:分片键的倾斜度必须用你自己的生产样本实测,本文给的所有阈值都是起算点;增长外推要用你自己的历史增长率,不要用行业均值。第二类是环境侧:磁盘与带宽余量要在你的实例上实测 IOPS 与吞吐后再套用第八节的估算公式,云盘、本地 NVMe、机械盘的差别是数量级的。第三类是版本侧:Vitess 的 sequence 语义、在线 DDL 行为、VReplication 的限速参数、跨分片事务的实现细节在不同版本间都发生过变化,务必对照你所用版本的发布说明逐项核对。第四类是业务侧:跨分片事务清单与 scatter 查询白名单这两项,只有真正熟悉业务 SQL 的人才能列全,任何外部文档都替不了这一步。
最后留一条判断供你带走:Vitess 的价值 = 它帮你抬升的容量上限,减去它在事务、查询、备份三个维度引入的复杂度。当你的单表还在千万行、峰值 QPS 还有一倍余量、团队没有专职 DBA、主要负载是复杂联表分析的时候,这个减法的结果是负的——请先做索引优化、读写分离、冷热分离与归档。当单表稳定过亿、写入 QPS 已经吃满单机、查询模式高度规整且绝大多数带分片键、团队有专人值守的时候,这个减法才会转正。分片的门槛从来不在部署,而在分片键选定的那一刻,以及在跨片查询与事务开始退化时的应对能力。
上一篇:2026 服务器租用自建统一身份认证 Keycloak 落地全解:Realm 模型、目录联邦与会话容量六维对比 + 避坑避雷手册
下一篇:2026 服务器租用自建日志平台 Graylog 部署全解:Elasticsearch 依赖、容量规划与留存周期六维对比 + 避坑避雷手册
Copyright © 2013-2020 idc10000.net. All Rights Reserved. 一万网络 科技有限公司 版权所有 深圳市科技有限公司 粤ICP备07026347号
本网站的域名注册业务代理北京新网数码信息技术有限公司的产品