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 套配置的差异根本理不清。
延伸阅读
- Go 实战:IM 系统:多机房部署与连接层容灾的对照实现
- 腾讯云架构实践:云数据库多可用区部署的官方参考
- MySQL 实战:分库分表订单系统:回到本书,1.4 节对齐中间件与云产品
