1.3 机器准备:MySQL 分片实例规格

分片方案定了 16 片,但「每个分片什么规格、花多少钱、放哪个可用区」还没数。云数据库 MySQL 的分片实例(TDSQL for MySQL 版)把这件事简化了很多,但规格估算仍然要自己做,拍脑袋的后果要么浪费成本、要么三个月后就扩容。这一节讲我们实际用到的估算方法。

场景:16 个分片怎么分配资源

先明确一个概念:云数据库 MySQL 的「分片实例」本身就是多分片架构,每个分片独立的计算与存储资源,对应用层是一个Endpoint。如果用自建 MySQL,16 个分片就是 16 组主从实例。两种路线在我们项目里都评估过,最终选了云数据库分片实例,原因是运维成本和容灾切换开箱即用。

估算的输入是三组数字:当前数据量与每月增量、峰值 QPS 与读写比、可接受的单查询响应时间。订单主表月增 2400 万行、平均行长 1.2KB,算下来每月新增约 30GB 数据量,加上索引与 binlog 空间要预留 2 倍。

估算项 数值 计算方式 备注
单分片容量上限 200GB 三年后总容量 / 16 留 30% 安全水位
单分片行数 约 4000 万行 三年 6 亿行 / 16 控制在单表 5000 万行内
单分片 QPS 约 400 总峰值 4200 加 30% 余量 / 16 读写分别核算
内存规格 32GB buffer pool 能装下单分片热数据 热数据约 60GB
磁盘规格 500GB 容量上限 2 倍以上 含日志与临时表空间

方案:分片规格与可用区部署

单分片热数据约 60GB 分布在 16 个片上,每片约 4GB 热数据,32GB 内存可以让 buffer pool 命中率稳定在 98% 以上。CPU 按每 1000 QPS 配 2 核的经验值,每片 2 核起步、大促分片 4 核。这些都是经验值,真实数值要以压测为准。

可用区部署上,16 个分片打散到两个可用区做交叉容灾:奇数分片在 A 区为主、偶数分片在 B 区为主,任何一个可用区整体故障,另一个区都保留一半分片的主节点。订单系统的容灾思路与 Go 实战:IM 系统 里的多机房连接层设计如出一辙:故障时降级一半流量总比全站不可用好。

这里补充一个容量估算的推导细节,很多人在这一步算错。订单总行数的预估要用「未来值」而不是「现在值」:当前 2 亿行,月增 2400 万行按三年 36 个月算合计约 10.6 亿行,但扣掉冷热分离每年归档走的 1 年前数据(约 8 亿行沉淀冷数据),主表三年后的规模约 6 亿行。除以 16 个分片得到约 4000 万行每片,恰好压在单表 5000 万行的舒适线以内,所以分片数定 16。如果去掉归档这一步直接按 10.6 亿行算,就需要 24 个分片。用未来值估算、同时把归档计划算进去,是避免「永远在扩容」的关键。

部署项 配置 目的
分片 0 到 7 主 可用区 A 承载一半分片写入
分片 8 到 15 主 可用区 B 交叉分布,单区故障保留一半
只读实例 每分片 1 个 报表与对账读流量
Redis 路由缓存 3 主 3 从 分片键到物理库的映射缓存,见 2.4 节
CLS 日志接入 全分片 慢查询与审计统一归集,见 4.3 节

新购云数据库分片实例可以参考 腾讯云云数据库特惠,先用小规格验证路由,再按压测结果升配。

配置:分片实例参数基线

分片实例与传统 MySQL 在参数上有一个重要差异:每个分片的参数是独立设置的,控制台批量修改后要逐片确认。下面是我们上线前统一刷过一遍的基线参数:

# 每个分片的基线参数(控制台参数模板下发)
innodb_buffer_pool_size = 20GB
innodb_buffer_pool_instances = 4
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000
sync_binlog = 100
binlog_row_image = MINIMAL
slow_query_log = ON
long_query_time = 0.5

innodb_io_capacity 一定要按云盘类型设置,SSD 云盘设 2000 比默认值 200 高一个数量级,不然刷脏速度跟不上写入。long_query_time 我们故意设成 0.5 秒而不是常见的 1 秒,目的是在分库早期积累更多慢查询样本,治理阶段见 4.3 节。

参数刷完后要做一次分片间一致性检查。16 个分片如果参数不一致(比如某片的 long_query_time 还是 1 秒),日志检索时就会出现「时有时无」的诡异现象。检查脚本很简单:

-- 在每个分片上执行,比对返回值是否一致
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW VARIABLES LIKE 'long_query_time';
SHOW VARIABLES LIKE 'sync_binlog';

云数据库分片实例可以在控制台用参数模板批量下发,但下发后仍有少数参数需要重启实例才生效(比如 innodb_buffer_pool_size)。我们把参数变更统一排在凌晨低峰窗口,每次只变更一半分片,避免重启造成整体抖动。

踩坑:规格按总量而不是按分片算

最典型的一次估算错误是 diskspace:用总容量 400GB 除以 16 得到单片 25GB 就买了 50GB 磁盘,结果上线一个月磁盘告警——因为 binlog 与 ib_logfile 也占空间,且云盘格式化后可用率约 90%。后来统一改成「数据量 2 倍加 30% 浮动」。

第二个坑是 CPU 与内存配比失衡。为了省钱选了 2 核 16GB,结果大促时 CPU 先打满,内存空着一半。云数据库实例的 CPU 与内存是有固定配比的,选定规格前先跑一轮压测看是 CPU 瓶颈还是 IO 瓶颈,钱要花在刀刃上。

第三个坑是只买了主实例没算只读实例的成本。报表与对账流量切到只读实例后,才发现只读实例与主实例同价(按规格计费),一个月成本直接翻倍。后来把非实时的对账任务改成凌晨跑批、复用主实例空闲时段,才把这部分成本压下来。分库之后一定要把只读实例、备份存储、流量三笔账一起算,否则预算永远在修订,这点在 5.4 节成本复盘有完整账单。

压测是规格选型唯一靠谱的依据,我们用「灌入 3 个月实际数据量、按峰值 QPS 的 1.5 倍压 4 小时」的方式验证每档规格。压测的关键不是看平均耗时,而是看 p99 耗时在第四个小时是否开始劣化——劣化说明 buffer pool 已经装不下热数据,该升内存了。

压测轮次 规格 平均耗时 p99 耗时 结论
第一轮 2 核 16GB 12ms 340ms CPU 先打满
第二轮 4 核 16GB 9ms 85ms 正常水位
第三轮 4 核 32GB 8ms 21ms 内存富余,可降配
最终选择 4 核 24GB 8ms 26ms 性价比平衡点

小结

规格估算的逻辑链是:业务增量推导容量与 QPS,除以分片数得到单分片负载,再按负载选 CPU、内存、磁盘。可用区交叉部署是分库项目的容灾标配。规格宁可初期略大,扩容迁移的成本远高于初期多花的实例费——这点在 5.1 节扩容章节还会详细展开。另外压测成绩只能代表当时的量,建议每半年复测一次,订单系统的流量曲线一旦突破压测时的 1.5 倍,就应该触发复测而不是等告警。

规格清单最终落库为一张机器台账,每个分片的实例规格、可用区、只读实例、参数模板版本都登记在案。扩容时按台账批量克隆新分片,Clone 出来的实例参数天然一致。这张台账在两年后扩容到 24 片时立了大功——没有它,16 套配置的差异根本理不清。

延伸阅读