企业出海多云关系型数据库架构实战:AWS RDS/Aurora、阿里云 RDS/PolarDB 与腾讯云 TDSQL 的高可用拓扑、读写分离与分库分表边界(2026最新版)
关系型数据库是多云架构里少数几个不能靠加机器解决的组件:它同时被事务语义、复制协议和连接模型三件事锁死。
- 前言:本文与既有文章的边界
- 四个决策变量:把「选型」还原成四个能回答的问题
- 三云产品族对照:同名不同物
- 高可用拓扑:从复制协议开始,而不是从产品名开始
- 读写分离:三类实现,三种完全不同的延迟
- 连接层:一个被低估的故障源
- 分库分表:先证明「非分不可」,再谈怎么分
- 跨云数据分布:冷备、热备,还是双写
关系型数据库是多云架构里少数几个不能靠加机器解决的组件:它同时被事务语义、复制协议和连接模型三件事锁死。这篇长文把「选哪个引擎」拆成四个决策变量——引擎族(自建兼容版 / 共享存储版 / 分布式版)、高可用拓扑(单可用区 / 多可用区主备 / 多可用区集群 / 跨地域)、扩展方式(纵向扩容 / 只读副本 / 分片)、连接模型(直连 / 代理 / 连接池),再逐项对照 AWS RDS 与 Aurora、阿里云 RDS 与 PolarDB、腾讯云 TencentDB 与 TDSQL 的实际能力边界。文中给出故障切换 RTO 的三段拆解、读写分离的延迟陷阱、分库分表的量化判据、一套可复算的成本算例,以及一份按现象定位的排障清单。所有价格只给量级与口径,不做汇率换算后的跨厂商排名。
关键词: 关系型数据库、高可用、读写分离、分库分表、RDS、Aurora、PolarDB、TDSQL、多云架构
一句话答案: 先别比价格,先回答四个问题:写入峰值到底多高、单库能不能扛三年、故障时业务能容忍几分钟不可写、应用能不能改连接串。这四个答案决定引擎族,引擎族决定拓扑,拓扑同时决定账单形态和故障形态。绝大多数误判都发生在第二问——用压测机房的 QPS 去推三年后的容量,而不是用业务增长曲线去推。
前言:本文与既有文章的边界
站内数据方向的文章已经覆盖了「搬」「护」「快」「隔离」,唯独没有覆盖源库本身长什么样。先把边界划清楚,避免把这篇读成某一篇的换皮:
| 既有文章 | 它解决的问题 | 与本文的边界 |
|---|---|---|
| 09-05 多云数据库跨云同步 | 数据怎么从 A 搬到 B(DTS / DMS / CDC) | 本文只决定数据放在哪种拓扑上,管道细节归 09-05 |
| 09-01 多云数据库安全加固与审计 | 账号、加密、审计日志怎么配 | 本文只把安全当控制点引用,不展开加固步骤 |
| 10-09 多云缓存架构 | 怎么用缓存把回源打下去 | 本文讨论被缓存保护的那一层,缓存一致性归 10-09 |
| 09-11 备份与勒索软件防护 | 恢复点能不能救回来 | 本文讨论在线可用性,备份保留与不可变归 09-11 |
| 10-21 SaaS 多租户隔离 | 租户之间的资源与数据边界 | 本文只说明实例级 / 库级 / 行级三种多租户落在数据库哪一层 |
| 09-23 数据治理平台 | 元数据、血缘、质量、数据网格 | 本文讨论引擎与拓扑,治理是它上面的一层 |
| 10-24 托管文件存储 | 非结构化共享目录 | 两者正交:一个存表,一个存文件 |
一句口诀把这几篇串起来:同步管搬运,备份管找回,缓存管少回源,治理管说得清,本文管扛得住。扛得住包含三层意思——故障时能切、流量涨时能扩、连接多时不会被打死。
一、四个决策变量:把「选型」还原成四个能回答的问题
大多数数据库选型讨论之所以吵不出结论,是因为大家在比产品,而产品是多维的。把它压成四个变量之后,讨论会迅速收敛,因为每个变量都有客观输入。
1.1 变量一:引擎族决定天花板在哪
三族的分界线不是「谁更先进」,而是写扩展靠什么:
- 自建兼容版(Amazon RDS、阿里云 RDS、TencentDB for MySQL):本质是托管了一台主备 MySQL/PostgreSQL。生态最成熟、改造成本最低、SQL 兼容性问题最少,代价是单库容量与写入能力被单机锁死。
- 共享存储版(Aurora、PolarDB、TDSQL-C):计算与存储分离,所有节点共享同一份分布式存储,只读节点不复制数据页、只回放日志。读扩展快、副本延迟低、扩容不用搬数据,但写入仍然只有一个写节点,写放大问题没有消失。
- 分布式版(PolarDB-X、TDSQL 分布式版、基于 ShardingSphere 的自建分片):把数据水平切开,写能力随分片数线性增长。代价是跨分片 JOIN、分布式事务、全局二级索引、扩容重分布这四件事都要单独设计。
| 维度 | 自建兼容版 | 共享存储版 | 分布式版 |
|---|---|---|---|
| 写扩展方式 | 纵向加规格 | 纵向加规格 | 加分片(近线性) |
| 读扩展方式 | 异步只读副本 | 共享存储只读节点 | 只读节点 + 分片副本 |
| 单库容量上限 | 受单机磁盘与备份窗口限制 | 大(自动增长,上限以官方为准) | 理论无上限 |
| 应用改造量 | 几乎为零 | 低(连接串与只读地址) | 高(分片键、跨片查询、事务) |
| 典型故障形态 | 主备切换、复制延迟 | 存储层多副本自愈 | 单分片热点、跨片事务悬挂 |
| 适合谁 | 中小规模、迁移优先 | 读多写少、要求快速切换 | 写入极高、单库确定要炸 |
1.2 变量二:高可用拓扑由 RTO 目标反推
拓扑不是越贵越好,而是先定 RTO/RPO,再选最便宜的那一档。四档常见形态:
- 单实例 + 快照:RTO 小时级(要重建实例、回放备份),RPO 等于上次备份点。只适合内部工具。
- 多可用区主备:同步或半同步复制,主库故障自动切到备库,RTO 分钟级,RPO 接近零。
- 多可用区集群(一写两读备):半同步 + 两个可读备节点,切换更快,且备节点能承接只读流量。
- 跨地域:异地副本或全球数据库,应对的是区域级故障,RTO 分钟到小时,RPO 秒级,成本最高。
1.3 变量三:扩展顺序不能颠倒
正确的顺序是先纵向、再读写分离、最后分片。原因很朴素:纵向扩容改一个规格参数、零代码改动;读写分离改的是连接串和少量路由逻辑;分片改的是数据模型,一旦分片键选错,回退成本接近重做。
跳过前两步直接分片的团队,绝大多数在半年后发现:写入 QPS 只涨了两倍,但跨分片查询和分布式事务带来的复杂度已经把团队吞掉。
1.4 变量四:连接模型是最容易被忽略的一层
单体应用时代,一个应用进程一条连接池就够。容器化 + Serverless 之后,实例数会在几分钟内从 20 涨到 200,每个实例开 20 条连接,数据库侧的连接数瞬间打满,表现为「CPU 不高、内存不高,但新连接全部超时」。
连接层的三种解法:应用内连接池(HikariCP / PgBouncer)、数据库代理(RDS Proxy、PolarDB 代理、TDSQL 数据库代理)、以及把长连接改成短事务。选择依据是连接数是稳定的还是脉冲式的——脉冲式必须上代理。
写入峰值 ──▶ 能不能单库扛三年?──否──▶ 分布式版(先设计分片键)
│是
▼
读 QPS 是否远大于写?──是──▶ 共享存储版 + 只读节点
│否
▼
RTO 要求 < 1 分钟?──是──▶ 多可用区集群 / 三节点
│否
▼
多可用区主备(性价比最高的一档)
二、三云产品族对照:同名不同物
下面这张表是本文的地基。注意最后两列——同名产品在不同云上的实现模型可能完全不同,这是跨云架构里最容易踩的坑:把 Aurora 的「读副本零数据复制」经验直接套到 RDS 只读副本上,会得到完全错误的延迟预期。
| 产品 | 引擎族 | 存储与复制模型 | 只读扩展上限 | 最适合 | 主要约束 |
|---|---|---|---|---|---|
| Amazon RDS(MySQL / PostgreSQL / MariaDB) | 自建兼容 | 实例本地 EBS,多可用区为同步复制 | MySQL/PG 最多 15 个只读副本 | 迁移优先、兼容性优先 | 单库容量与写 QPS 受单机限制 |
| Amazon RDS(SQL Server / Oracle) | 自建兼容 | 同上 | 5 个只读副本 | 传统商业数据库上云 | 许可与 BYOL 条款另算 |
| Amazon Aurora MySQL / PostgreSQL | 共享存储 | 6 份副本跨 3 个可用区,日志即数据库 | 15 个只读副本 | 读多写少、要求秒级切换 | 写入仍单节点;I/O 按请求计费 |
| Aurora Serverless v2 | 共享存储 + 弹性算力 | 以 ACU 为粒度伸缩 | 同 Aurora | 波动负载、开发测试环境 | 缩容有冷却时间,持续高写不划算 |
| 阿里云 RDS MySQL 高可用版 | 自建兼容 | 主备跨可用区,半同步 | 只读实例(上限随规格) | 传统业务上云 | 主备切换期间有秒级不可写 |
| 阿里云 RDS 三节点企业版 | 自建兼容 + 三副本 | 一主两备,同城三可用区 | 只读实例 | 金融级 RPO=0 | 成本显著高于双节点 |
| 阿里云 PolarDB MySQL 版 | 共享存储 | 计算存储分离,存储多副本 | 最多 15 个只读节点 | 读扩展 + 大容量单库 | 写单节点;需正确配置代理 |
| 阿里云 PolarDB-X | 分布式 | 计算节点与存储节点分层 | 随分片水平扩展 | 高并发写入、透明分库分表 | 跨分片查询有额外开销 |
| 腾讯云 TencentDB for MySQL(双节点 / 三节点) | 自建兼容 | 主备异步/半同步;三节点强同步 | 只读实例 | 通用业务、政务 | 三节点成本更高,网络要求更严 |
| 腾讯云 TDSQL-C MySQL 版 | 共享存储 | 计算存储分离 | 最多 15 个只读节点 | 读扩展、快速弹性 | 存储按量计费,需盯住增长 |
| 腾讯云 TDSQL(分布式) | 分布式 | 水平拆分 + 强同步 | 随分片水平扩展 | 金融核心、海量写入 | 分片键设计决定一切 |
读这张表要抓住三个不对称:
- 只读副本的语义不对称:RDS 只读副本是异步复制 binlog,Aurora 与 PolarDB 的只读节点共享存储、只回放日志。前者延迟随写入量波动(大事务能拖到几十秒),后者通常在毫秒到百毫秒量级。所以「加只读副本解决读压力」这句话,在三族里的含义完全不同。
- 故障切换的机制不对称:多可用区主备靠 DNS 切换 + 备库提升,客户端要重连;共享存储版靠存储层选主,切换更快但仍有连接中断。
- 计费维度的不对称:自建兼容版按「实例规格 + 存储 + 备份」计费;共享存储版多出「I/O 请求数」这一维;分布式版还多出「分片数 × 节点规格」。第三种的账单最难预测,因为分片数会随业务增长被动增加。
口径说明:本节的「上限」均为能力级描述,具体规格(最大存储、最大连接数、只读节点上限随规格的变化)以各厂商当期文档为准。文中不引用未核实的精确数字,宁可口径保守,也不印一个可能过期的上限。
三、高可用拓扑:从复制协议开始,而不是从产品名开始
高可用的实际表现由复制协议决定,产品名只是包装。三种协议对应三种故障形态,先把它们分开。
3.1 三种复制协议的真实差别
| 协议 | 提交返回时机 | RPO | 写延迟代价 | 失效模式 |
|---|---|---|---|---|
| 异步复制 | 主库本地提交后立即返回 | 可能丢最后一段事务 | 几乎为零 | 主库宕机丢失未同步 binlog |
| 半同步复制 | 至少一个备库收到并落盘日志后返回 | 接近零(取决于等待点) | 增加一次跨可用区往返 | 备库无响应时退化为异步 |
| 强同步 / 多数派 | 多数派副本确认后返回 | 零 | 增加多次往返 | 少数派故障不影响提交 |
半同步有一个必须知道的细节:MySQL 的半同步插件在等待超时后会静默退化为异步。默认超时是 10 秒,也就是说备库网络抖动超过 10 秒,主库会继续接受写入,此时的 RPO 已经不再为零,但监控面板上一切正常。这就是为什么「我们开了半同步」不等于「我们 RPO=0」——要盯的是退化事件,不是开关状态。
MySQL 8.0.26 之后,半同步的变量名从 master 改为 source,等待点默认是 AFTER_SYNC(事务在引擎层提交前等待备库确认),比旧版的 AFTER_COMMIT 少一个「主库已提交但备库未收到」的窗口。做跨云复制评估时,这一项要单独确认,因为它直接决定切换后会不会出现主备数据分叉。
3.2 四档拓扑的量化对照
| 拓扑 | 复制方式 | RPO | RTO(经验值) | 相对成本 | 适用场景 |
|---|---|---|---|---|---|
| 单实例 + 定时快照 | 无 | 上次快照点 | 小时级 | 1.0 | 内部系统、可重建数据 |
| 多可用区主备 | 同步(AWS 多可用区实例)/ 半同步 | ≈0 | 通常 1–2 分钟 | ≈2.0 | 绝大多数生产业务 |
| 多可用区集群(一写两读备) | 半同步 | ≈0 | 通常 35 秒内 | ≈2.5–3.0 | 切换敏感、备节点可分担只读 |
| 同城三副本(三节点企业版) | 强同步(多数派) | 0 | 秒级到 1 分钟 | ≈3.0 | 金融、政务、有明确 RPO=0 要求 |
| 跨地域(全球数据库 / 异地灾备) | 物理复制 / 异步 | 秒级 | 分钟到小时 | 4.0 以上 | 区域级故障、跨境业务 |
表中 RTO 是厂商公开口径与工程经验叠加的量级,不是 SLA 承诺。真实 RTO 一定比你想象的长,因为切换只完成了「数据库能写」,没有完成「应用能连」。
3.3 把 RTO 拆成三段,才知道该优化哪里
线上事故里最常见的误判,是把数据库的切换时间当成业务恢复时间。实际 RTO 是三段之和:
检测故障 ──▶ 选主与提升 ──▶ 客户端重连与预热
(10~60s) (10~40s) (30s~5min)
- 检测段:依赖健康检查间隔与失败阈值。调得太灵敏会误切(网络抖动就切),太迟钝会拖长不可用时间。经验值是连续 3 次失败、间隔 10 秒。
- 选主段:厂商托管,多可用区主备通常 60–120 秒,多可用区集群可压到 35 秒内,共享存储版通常在 30 秒量级。
- 重连段:这是自建方案里最容易被忽略、也最容易爆炸的一段。DNS 缓存未过期、连接池里全是死连接、JVM 里的旧连接对象没有失效检测,应用会在数据库已经恢复之后继续报错几分钟。这一段只能靠应用侧解决:连接池开启
validationTimeout与keepaliveTime,DNS 记录 TTL 调到 30 秒以内,重试带指数退避。
一个反直觉的结论:优化重连段通常比升级数据库拓扑更划算。把 RTO 从 120 秒压到 35 秒要多花一倍钱;把重连段从 5 分钟压到 30 秒只需要改几行连接池配置。
3.4 多可用区不等于跨云容灾
这是多云架构里最贵的认知错误。三种「高可用」应对的是三种完全不同的故障:
| 故障类型 | 多可用区能救 | 跨地域能救 | 跨云能救 |
|---|---|---|---|
| 单台物理机 / 单可用区故障 | 能 | 能 | 能 |
| 区域级故障(地震、区域网络) | 不能 | 能 | 能 |
| 云厂商账号级故障(欠费、误删、管控面故障) | 不能 | 不能 | 能 |
| 厂商级产品缺陷(内核 bug、批量重启) | 不能 | 部分能 | 能 |
如果你的合规要求写的是「业务连续性」,多可用区就够了;如果写的是「厂商级风险隔离」,那必须跨云。而跨云的代价不是多一份实例钱,而是双写一致性与切换决策权——这部分本文第八节展开。
3.5 切换演练:不演练的高可用等于没有
托管数据库的切换能力是厂商给的,但你的应用能不能接住只有演练能验证。建议的节奏:
- 每季度一次计划内切换演练,选业务低峰,提前通知上下游。
- 每次演练记录四个数字:检测耗时、切换耗时、首笔成功事务时间、错误率回到基线时间。
- 演练后必须复盘「重连段」,这一段几乎每次都能发现新问题(缓存了旧 IP 的服务、没有重试的定时任务、只连一次的消息消费者)。
一个可直接抄的判据:如果团队说不出上一次切换演练的日期,那么这个高可用拓扑在事故当天的表现,等同于单实例。
四、读写分离:三类实现,三种完全不同的延迟
「加只读副本」是数据库扩容里最常见、也最容易做错的一步。做错的原因不是配置,而是没搞清楚读到的数据可能是旧的。
4.1 三种实现路径
| 实现层 | 代表方案 | 路由灵活性 | 一致性控制 | 主要代价 |
|---|---|---|---|---|
| 应用层 | 多数据源 + AOP / 注解路由 | 最高(可按业务强制走主) | 自己实现 | 代码侵入,容易漏配 |
| 中间件层 | ProxySQL、MaxScale、MyCat、云厂商数据库代理 | 中(按 SQL 类型或权重) | 代理提供一致性级别 | 多一跳网络,代理本身要 HA |
| 数据库内核 | Aurora 只读副本与 reader endpoint、PolarDB 只读节点、TDSQL-C 只读实例 | 低(按 endpoint 轮询) | 依赖复制延迟 | 无法按业务语义区分 |
三个容易搞错的点:
- Amazon RDS Proxy 不做读写分离。它的职责是连接池、故障切换加速和 IAM 认证,读流量仍然打到你给它的那个 endpoint。想要读写分离,得自己用只读副本 endpoint 或上中间件。
- Aurora 的 reader endpoint 是轮询,不感知复制延迟。也就是说一个落后 30 秒的只读副本照样会被分到读流量。要按副本规格或负载分流,得用自定义 endpoint 分组。
- 云厂商的数据库代理通常提供「一致性级别」开关。会话一致性会在写后一段时间内把该会话的读强制回主库,全局一致性则更保守——级别越高,回主库的读越多,主库压力越大。这是典型的「一致性换吞吐」旋钮,不是免费开关。
4.2 复制延迟到底从哪来
只读副本的延迟不是均匀分布的,它有四个典型来源:
- 大事务:一条影响百万行的 UPDATE,在主库上是一条语句,在备库上要回放百万行。期间延迟曲线是一条尖峰,能到几十秒。
- DDL:加索引、改字段类型这类操作在主库上可能是 Online DDL,但在备库上是单线程回放,大表能堵几分钟。
- 并行回放能力:MySQL 5.6 按 schema 并行,单库业务等于没有并行;5.7 起按组提交(LOGICAL_CLOCK)并行;8.0 默认 WRITESET,冲突检测更细,并行度更高。从 5.6 升到 8.0,最直接的收益之一就是复制延迟显著下降。
- 备库规格不足:主库 8 核、备库 4 核,写入一高备库就先顶不住,延迟随负载线性上升。
监控上,Seconds_Behind_Source(旧版本叫 Seconds_Behind_Master)精度只到秒,而且在大事务期间会显示 0(因为它按最后一个已执行事件的时间戳计算)。更可靠的指标是 performance_schema.replication_applier_status_by_worker 里的 APPLYING_TRANSACTION 位置差,以及 MySQL 8.0 的 replication_applier_status_by_coordinator。
-- 秒级延迟,NULL 表示复制线程已中断
SHOW REPLICA STATUS;
-- 细粒度:每个 worker 正在回放哪个事务
SELECT WORKER_ID, APPLYING_TRANSACTION, LAST_APPLIED_TRANSACTION
FROM performance_schema.replication_applier_status_by_worker;
-- 长事务造成的 purge 阻塞,历史列表长度持续上涨说明有大事务
SHOW ENGINE INNODB STATUS;
4.3 哪些读必须强制走主库
| 场景 | 是否必须走主 | 原因 |
|---|---|---|
| 写入后立即跳转详情页 | 是(或会话粘滞 N 秒) | 用户会看到自己刚改的数据没生效 |
| 支付回调后的订单状态查询 | 是 | 状态机判断依赖最新值,读到旧值会重复处理 |
| 库存扣减后的余量展示 | 是 | 超卖投诉的常见来源 |
| 幂等校验(查唯一键是否存在) | 是 | 备库还没回放,幂等直接失效 |
| 列表页、报表、导出 | 否 | 可容忍秒级延迟,且是读放大的主力 |
| 全文检索类查询 | 否 | 建议直接放搜索引擎,不要压数据库 |
工程上最省事的做法是会话粘滞:同一会话在写操作后的 N 秒内(N 取该业务可接受的最大延迟,通常 1–5 秒)全部走主库,之后恢复读分流。它比「按 SQL 语义判断」实现简单得多,也几乎不会漏。
4.4 只读副本的成本不止实例费
三个隐藏成本:
- 每个副本都是一份完整实例费。共享存储版稍好(不复制数据页,存储只算一份),自建兼容版则是每个副本一份存储 + 一份计算。
- 跨地域副本的复制流量单独计费,量级是每 GB 几分美元。一个日增 50 GB 的库,跨区复制一个月就是 1.5 TB 的出流量。
- 备份与快照成本随副本数上升(部分厂商只对主实例做自动备份,副本不额外计费,需逐家确认)。
所以正确的做法不是「读不够就加副本」,而是先看读流量里有多少是可以缓存的、多少是可以走搜索引擎的、剩下多少才真正需要数据库只读节点。10-09 那篇缓存架构讲的正是前两步。
五、连接层:一个被低估的故障源
数据库连接是有状态、有内存、有上限的资源。它不像无状态的 HTTP 请求,加个负载均衡就完事。连接层的容量算错,表现通常是「数据库监控一片绿,业务全线超时」。
5.1 先算清楚需要多少连接
经验公式是:并发连接数 ≈ 峰值 QPS × 平均事务耗时(秒)。注意是峰值,不是均值,也不是「QPS 除以单连接能扛的 QPS」。
举例:峰值 3,000 QPS、平均事务 8 毫秒,那么理论上同时占用连接的请求约 24 个。考虑到突发与长事务,连接池开 50 足够。但很多团队按「1000 QPS 就开 1000 连接」来配,结果是数据库把大量内存花在连接缓冲上,缓存命中率下降,性能反而更差。
连接池大小的经典起点是 HikariCP 的建议公式:连接数 = CPU 核数 × 2 + 磁盘数。4 核数据库配 9–12 条连接,听起来少得离谱,但在 OLTP 场景下往往比 200 条连接吞吐更高——因为连接一多,锁竞争和上下文切换就会吃掉收益。
5.2 每条连接要花多少内存
MySQL 每条连接的内存占用由会话级缓冲决定,典型项包括 read_buffer_size、sort_buffer_size、join_buffer_size、binlog_cache_size。空连接只占几百 KB,但活跃连接在排序、JOIN 时会各自分配缓冲区,峰值可以到每条 5–10 MB。
于是出现一个反直觉现象:连接数上限设成 2,000,实际能安全承载的活跃连接可能只有 300——超过之后不是报「连接数超限」,而是内存吃紧、swap、响应时间雪崩。连接数上限应该按内存倒推,而不是按最大值配。
5.3 四种连接层方案的取舍
| 方案 | 作用 | 适合 | 不适合 | 注意 |
|---|---|---|---|---|
| 应用内连接池(HikariCP / Druid) | 复用连接、检测失效 | 单体或实例数稳定的服务 | 实例数剧烈波动的场景 | 每个进程一份池,实例数一涨连接数线性上涨 |
| PgBouncer | PostgreSQL 连接复用,模式可选 | 大量短连接、函数计算 | 依赖会话状态的应用 | transaction 模式下 prepared statement 与 advisory lock 有坑 |
| 云数据库代理(RDS Proxy / PolarDB 代理 / TDSQL 代理) | 连接池 + 故障切换加速 + 读写分离 | Serverless、容器弹性伸缩 | 强依赖会话变量的老应用 | 代理本身要开多可用区,否则它成为新单点 |
| 中间件(ProxySQL / MaxScale) | 读写分离、查询缓存、连接多路复用 | 自建 MySQL 集群 | 无专职 DBA 的团队 | 多一跳网络,规则配置出错会放大故障 |
PgBouncer 的三种池模式值得单独记住,因为选错会引发极难排查的 bug:
| 模式 | 连接归还时机 | 兼容性 | 典型故障 |
|---|---|---|---|
| session | 会话结束 | 最好 | 连接复用率低 |
| transaction | 事务结束 | 较好 | 会话级 SET、临时表、advisory lock 会跨事务串味 |
| statement | 每条语句结束 | 最差 | 显式事务直接失效 |
transaction 模式最常用,但它把「会话」压缩成了「事务」,任何依赖会话状态的东西都会出问题——包括 SET search_path、SET timezone、临时表、以及某些 ORM 的 prepared statement 缓存。这也正是 10-21 那篇多租户文章里 RLS 泄漏的同类根因:连接被复用,但会话状态没有被重置。
5.4 Serverless 与弹性扩容下的连接风暴
函数计算、Kubernetes HPA、Serverless 容器有一个共同特征:实例数在几十秒内成倍变化。每个新实例启动时都会建连接,旧实例缩容时连接不会立刻释放(TCP 层还在 FIN_WAIT)。两个方向叠加,数据库侧看到的连接数会出现远超业务量的尖峰。
三道防线:
- 代理前置:所有弹性工作负载统一走数据库代理,应用侧看到的是代理的连接,真实连接数被池化。
- 连接上限 + 排队:在代理或连接池上设硬上限,超出的请求排队而不是直接失败,配合超时快速失败,避免请求堆积。
- 缩容优雅退出:实例收到终止信号后先归还连接再退出,把
terminationGracePeriodSeconds设得比连接池的maxLifetime长一点。
5.5 连接泄漏怎么定位
连接泄漏的表现是「连接数缓慢上涨、几天后打满、重启应用就好」。定位靠三张表:
-- MySQL:按来源 IP 与状态聚合,找出只增不减的连接
SELECT SUBSTRING_INDEX(HOST, ':', 1) AS client_ip, COMMAND, COUNT(*) AS conns
FROM information_schema.PROCESSLIST
GROUP BY client_ip, COMMAND
ORDER BY conns DESC;
-- 找出空闲时间最长的连接(SLEEP 时间即空闲秒数)
SELECT ID, USER, HOST, DB, TIME, STATE FROM information_schema.PROCESSLIST
WHERE COMMAND = 'Sleep' ORDER BY TIME DESC LIMIT 20;
-- PostgreSQL:按应用名与状态聚合
SELECT application_name, state, count(*) FROM pg_stat_activity
GROUP BY application_name, state ORDER BY count(*) DESC;
如果 Sleep 状态的连接集中在某个应用名上,基本可以断定是那边的连接池没有正确关闭,或者 maxLifetime 设成了无限。
六、分库分表:先证明「非分不可」,再谈怎么分
分片是数据库架构里唯一基本不可逆的决策。分片键选错,回退成本接近重做一次业务。所以这一节的重点不是「怎么分」,而是「怎么证明现在还不该分」。
6.1 先排除三种不需要分片的情况
在讨论分片之前,先确认下面三件事都做完了:
- 索引是否真的对。慢查询日志里 80% 的问题来自缺失索引或索引失效(隐式类型转换、函数包裹列、最左前缀断裂),这些都能通过加索引解决,不需要动数据模型。
- 冷数据是否归档。订单表 5 亿行里,往往 80% 是两年前的只读数据。把它们迁到归档库或对象存储,主表立刻瘦身,效果比分片更直接。
- 是否可以用分区表。MySQL 8.0 与 PostgreSQL 都支持原生分区,按时间分区后,历史分区可以独立归档、独立 DDL,运维成本远低于应用层分片。
这三步做完仍然不够,才轮到分片。
6.2 量化判据表
| 信号 | 经验阈值 | 为什么它是信号 |
|---|---|---|
| 单表行数 | 2,000 万 ~ 1 亿 | B+ 树层数从 3 层涨到 4 层,每次查询多一次随机 I/O |
| 单库存储 | 2 ~ 4 TB | 备份窗口、DDL 时长、恢复时间都随容量线性恶化 |
| 写入 QPS | 持续超过单实例上限的 60% | 留 40% 余量给突发与主备切换后的单点承压 |
| 单表 DDL 耗时 | 超过 30 分钟 | 意味着任何结构变更都需要停业务窗口 |
| 连接数 | 持续超过上限的 70% | 分片后连接数会按分片数成倍增长,先解决连接层 |
| 单表索引大小 | 超过内存的 50% | 索引无法全部缓存,随机读性能断崖 |
关键点是:行数不是唯一信号。一张 5,000 万行但只按主键查的表,往往比一张 500 万行但有 8 个二级索引、写入频繁的表更健康。
6.3 分片键决定一切
| 分片键 | 优点 | 缺点 | 适用 |
|---|---|---|---|
| 用户 ID / 租户 ID | 同一用户数据聚合,事务在单分片内完成 | 大客户会造成分片倾斜(热点租户) | SaaS、面向 C 端的账户体系 |
| 订单 ID(雪花) | 写入均匀 | 按用户查订单要扫全部分片 | 订单中心(配合基因法) |
| 时间 | 归档方便,旧分片可下线 | 写热点永远在最新分片,等于没分 | 日志、流水类只写少查 |
| 商户 ID | 商户维度查询快 | 头部商户热点 | 电商平台侧 |
| 哈希(手机号 / 邮箱) | 均匀 | 范围查询失效 | 用户中心 |
两个必须掌握的技巧:
- 基因法:把用户 ID 的低位比特嵌入订单 ID,使「按订单 ID 查」和「按用户 ID 查」落到同一分片。代价是 ID 生成器要改造,收益是省掉一张「订单-用户」映射表。
- 热点租户隔离:分片键是租户 ID 时,头部租户会独占一个分片。解法是给大租户单独指定物理分片,其余租户按哈希分布——这就是 10-21 那篇里 Pool / Bridge / Silo 模型在数据库层的投影。
6.4 分片之后必须重写的五件事
| 问题 | 表现 | 常见解法 |
|---|---|---|
| 跨分片 JOIN | 查询变慢或报错 | 冗余字段、宽表、把 JOIN 移到应用层 |
| 聚合与排序分页 | ORDER BY ... LIMIT 结果错乱 | 各分片取前 N 再归并;深度分页改用游标 |
| 分布式事务 | 跨片写入不一致 | 尽量避免跨片事务;必须时用 Saga 或 TCC |
| 全局唯一 ID | 自增主键冲突 | 雪花算法 / 号段模式 / UUIDv7 |
| 扩容重分布 | 数据搬迁期间双写 | 预分逻辑槽(如 1024 槽),扩容只搬槽不重算 |
预分槽是最值得提前做的一件事:一开始就把数据分成 1,024 个逻辑槽,物理上放在 4 个库,每个库 256 槽。将来扩到 8 个库,只需把每个库的一半槽搬走,路由规则不变,搬迁量可控。相比之下,「按 id % 4 直接取模」在扩容到 8 库时,几乎每条数据都要换位置。
6.5 中间件与分布式数据库怎么选
| 方案 | 形态 | 侵入性 | 分布式事务 | 运维负担 | 适合 |
|---|---|---|---|---|---|
| ShardingSphere-JDBC | 客户端 SDK | 高(改依赖与配置) | 支持(XA / BASE) | 低(无额外组件) | Java 技术栈、团队有能力改代码 |
| ShardingSphere-Proxy | 独立代理 | 低(当成 MySQL 用) | 支持 | 中(代理要 HA) | 多语言、不想改应用 |
| Vitess | 代理 + 管控面 | 低 | 有限支持 | 高 | 大规模 MySQL、有平台团队 |
| PolarDB-X | 托管分布式数据库 | 低(兼容 MySQL 协议) | 支持 | 低(厂商托管) | 已在阿里云、想少运维 |
| TDSQL 分布式版 | 托管分布式数据库 | 中(需设计分片键) | 强一致 | 低 | 金融核心、强一致要求 |
选择逻辑很清晰:团队有没有能力自己运维分片中间件。有能力,ShardingSphere 或 Vitess 更灵活、不被厂商绑定;没有能力,托管分布式数据库的综合成本更低,代价是迁移成本随数据量上升。
6.6 一条务实的路线
索引优化 + 慢查询治理(1~2 周)
│ 仍不达标
▼
冷数据归档 + 分区表(2~4 周)
│ 写入仍打满
▼
读写分离 + 连接代理(1~2 周)
│ 写入仍打满
▼
预分槽分片 / 分布式数据库(1~3 个月,需专项)
跳过前三步直接做第四步的团队,通常会在分片完成后发现:慢查询还在,只是从「单表慢」变成了「跨片更慢」。
七、跨云数据分布:冷备、热备,还是双写
多云的数据库层绕不开一个选择:第二个云上的那份数据,是备着还是在用。这三档的差别不在技术难度,而在一致性责任由谁承担。
7.1 三种形态
| 形态 | 复制方式 | RPO | RTO | 一致性责任 | 成本倍数 |
|---|---|---|---|---|---|
| 冷备 | 定时全量 + binlog 归档到对象存储 | 小时级 | 数小时(要重建实例再回放) | 厂商 | 1.1 ~ 1.3 |
| 温备 / 热备 | 异步或半同步复制到另一云的实例 | 秒级 | 分钟级(提升副本) | 厂商 + 你的切换脚本 | 2.0 |
| 双活双写 | 两个云都能写 | 冲突时才可见 | 秒级 | 你的应用 | 2.5 以上 |
第三档看起来最美,实际最难。原因在 7.2。
7.2 为什么数据库双写几乎总是错的
- 冲突没有通用解。两个云同时改同一行,最后写的赢?按时间戳?按版本号?任何策略都要业务定义,而业务往往说「不应该发生」。它会发生。
- 自增与序列冲突。两边都用自增主键,迟早撞号。改成号段或雪花之后,又引入时钟回拨问题。
- 事务边界被撕裂。一个事务里写了订单表和库存表,两张表分处两个云,本地事务变成分布式事务,延迟从毫秒变成几十毫秒。
- 跨云延迟放大了锁持有时间。同区域内跨云 RTT 通常在几毫秒,跨地域 50–200 毫秒。一个持有行锁 3 次的写事务,在跨云双写下可能持锁几百毫秒,锁等待迅速堆积。
所以工程上更常见的做法是「单写多读」:写只落一个云,另一个云只提供读与灾备接管。切换是单向的、有明确决策点的,而不是两边同时活着。
7.3 单元化:把「跨云」变成「跨单元」
真正能同时拿到低延迟与故障隔离的,是单元化:按用户 ID 或租户 ID 把流量与数据切成一闭环的单元,每个单元内部自洽(应用 + 数据库 + 缓存),跨单元只做异步数据同步(通常是用户维度的镜像或汇总)。
这里要和 10-02 那篇划清边界:那篇讲的是部署拓扑与故障域收敛,本文讲的是数据怎么按这个拓扑切分与路由。两者是同一件事的两面,顺序上应该先定单元边界,再决定每个单元用哪种数据库拓扑。
单元化的代价是「跨单元查询」:全局报表、全局搜索、跨单元转账都要额外设计。所以它适合用户维度天然可切的业务(社交、SaaS、电商买家侧),不适合全局强一致的账务核心。
7.4 跨云复制的五个检查点
| 检查点 | 要确认什么 | 典型坑 |
|---|---|---|
| 复制延迟 | 常态延迟与峰值延迟分开看 | 只看均值,忽略大事务尖峰 |
| 带宽成本 | 每日增量 × 跨区单价 | 忘了索引变更会带来额外 binlog 量 |
| DDL 同步 | 结构变更怎么同步到对端 | 主库 Online DDL,备库阻塞回放 |
| 字符集与时区 | 两端必须一致 | 一端 utf8 一端 utf8mb4,表情符号写入失败 |
| 自增与序列 | 切换后是否连续、是否冲突 | 双写时撞号,单写时切换后跳号被当成 bug |
一个常被问的问题:跨云热备的 RTO 到底是多少?答案取决于你的切换脚本,而不是数据库。数据库提升副本可能只要 1 分钟,但改 DNS、改连接串、验证应用、回滚预案,通常是 30 分钟到 2 小时。把这段写进预案,比优化数据库切换时间更有价值。
八、兼容性评估:迁移前必须逐项打勾的八件事
跨云或从自建迁到托管,出问题的地方几乎从不是「SQL 跑不起来」,而是这些边角:
| 项目 | 要确认的细节 | 出问题的表现 |
|---|---|---|
| 版本 | 5.7 还是 8.0,小版本差异 | 8.0 默认 utf8mb4_0900_ai_ci 排序规则,与 5.7 的 utf8mb4_general_ci 结果不同 |
| 字符集 | 库、表、列、连接四层是否统一 | 只有连接层是 utf8,插入 emoji 报错 |
| 排序规则 | 大小写敏感、重音敏感 | 唯一索引在两端判重结果不一致 |
| 存储引擎 | InnoDB 为主,是否有 MyISAM 遗留表 | MyISAM 不支持事务,迁移后行为变化 |
| SQL 方言 | LIMIT、IFNULL、GROUP BY 严格模式 | ONLY_FULL_GROUP_BY 打开后老 SQL 直接报错 |
| 存储过程 / 触发器 / 事件 | 是否有、是否需要 DEFINER 权限 | 迁移工具跳过或执行失败 |
| 权限模型 | 账号、角色、GRANT 语法 | 迁移后应用账号权限不足 |
| 时区 | 服务端、会话、应用三处 | 时间字段偏移 8 小时 |
最容易被低估的是排序规则。它不影响功能测试,只影响 WHERE name = 'abc' 能不能命中、唯一索引会不会误判重复。上线后表现为「偶发重复注册」「查询少一条」,排查成本极高。
评估的做法不是读文档,而是跑一遍真实 SQL:把生产慢查询日志里的 Top 200 条 SQL 拿到目标实例上 EXPLAIN,对比执行计划与耗时。这一步通常能在半天内暴露 90% 的兼容性问题。
九、上线前必须做的四个压测
压测的目的不是拿到一个漂亮的 QPS 数字,而是知道系统在什么条件下开始不线性。四个必做项:
9.1 四项压测
| 压测 | 怎么做 | 要回答的问题 |
|---|---|---|
| 基准 OLTP | sysbench 读写混合,逐步加压到拐点 | 单实例的真正吞吐上限在哪 |
| 写入尖峰 | 固定并发,突增 3 倍写入并维持 5 分钟 | 复制延迟会不会失控,连接池会不会打满 |
| 主备切换 | 主动触发托管实例的故障切换,全程打流量 | 应用要多久恢复,错误率曲线长什么样 |
| 大查询干扰 | 在压测中并发跑一条大报表 SQL | 一条慢查询会把整体 P99 拖到什么程度 |
sysbench 的常用组合(数据量要大于内存,否则缓存全命中,结果没有参考价值):
sysbench oltp_read_write --db-driver=mysql --mysql-host=HOST --mysql-user=USER \
--mysql-password=PASS --mysql-db=sbtest --tables=20 --table-size=5000000 \
--threads=64 --time=300 --report-interval=10 prepare
sysbench oltp_read_write --db-driver=mysql --mysql-host=HOST --mysql-user=USER \
--mysql-password=PASS --mysql-db=sbtest --tables=20 --table-size=5000000 \
--threads=64 --time=300 --report-interval=10 run
9.2 看四个指标,不看平均值
- P99 而非平均:平均值永远好看,用户感知的是尾部。
- 复制延迟峰值:压测期间的
Seconds_Behind_Source最大值,比均值重要一个数量级。 - 连接数:是否触到上限,是否出现排队。
- IOPS 饱和度:gp3 的基准 IOPS 是 3,000、基准吞吐 125 MiB/s,超出后靠突发积分,积分耗尽就掉速——这正是「压测很好、上线三天变慢」的典型原因。
9.3 四个压测陷阱
- 数据量太小:100 万行全在 buffer pool 里,等于在测内存。
- 只有均匀分布:真实业务有热点(大客户、爆款商品),均匀压测测不出热点行锁。
- 忽略复制:只压主库,上线后才发现只读副本延迟 30 秒。
- 用
SELECT 1测连接上限:连接建立与查询执行是两条路径,前者测不出后者的内存问题。
十、成本模型:四个维度与四个隐藏项
云数据库的账单从来不是「一台机器的钱」。它是四个维度的乘积:计算(规格 × 节点数 × 时长)+ 存储(容量 × 单价)+ I/O(请求数,仅共享存储版)+ 附加(备份、跨区流量、日志、监控)。
10.1 算例:同一套业务,两种引擎族
假设业务需要:4 vCPU / 32 GiB 主库、多可用区、2 个同规格只读、2 TB 数据、每月 2 亿次读 I/O。
| 成本项 | 自建兼容版(RDS 多可用区) | 共享存储版(Aurora) |
|---|---|---|
| 主实例(按需,含多可用区双份) | $700.80 | $350.40 |
| 只读副本 × 2 | $700.80 | $700.80 |
| 存储 2,000 GB | $230.00(gp3) | $200.00 |
| I/O(2 亿次) | 含在存储里 | $40.00 |
| 备份(超出免费额度 1,000 GB) | $95.00 | $95.00 |
| 合计(月) | $1,726.60 | $1,291.20 |
| 合计(年) | $20,719.20 | $15,494.40 |
两个结论:
- 在这个配置下,共享存储版便宜约 25%(比值 1.34),主要来自多可用区不额外收一份计算费——它的高可用由存储层副本承担,不需要一个全职待命的备库。
- 但这个比较对写入密集业务不成立:共享存储版的 I/O 按请求计费,写入量一大,第四行会迅速膨胀;而自建兼容版的存储单价是固定的。
口径:以上单价为 us-east-1 按需量级(2026 年口径),仅用于说明成本结构,不代表任何厂商报价;实际价格随区域、引擎版本、付费方式变化,请以官网价目页为准。换算成人民币时不要直接乘汇率做排名——跨境业务的真实成本还包括合规、带宽与人力。
10.2 四个隐藏项
| 隐藏项 | 什么时候出现 | 量级 |
|---|---|---|
| 跨地域复制流量 | 开跨区只读副本或全球数据库 | 每 GB 几分美元,日增 50 GB 的库每月约 1.5 TB |
| 备份超出额度 | 自动备份保留期拉长、快照长期保留 | 与数据量同阶,长期快照能超过实例费 |
| 日志与监控 | 慢日志、审计日志投递到日志服务 | 按写入量计费,审计全开时不可忽略 |
| I/O 请求 | 共享存储版 + 高频小写入 | 每月上亿次后成为主要成本项 |
10.3 省钱的正确顺序
- 关掉闲置副本:很多团队挂着 3 个只读副本,实际只有一个在承接流量。看连接数与 QPS 就能确认。
- 右规格化:连续两周 CPU 峰值低于 20%、内存低于 40% 的实例,降一档规格通常无损。
- 预留 / 节省计划:长期稳定的主库用 1 年期预留,折扣通常在三成量级;波动部分留给按需。
- 存储分层与归档:把两年前的冷数据迁走,既省存储也省备份。
- 最后才考虑换引擎族:迁移成本通常吃掉两到三年的差价。
十一、IaC:三云 RDS 的最小骨架
跨云架构最忌讳「控制台点出来的配置」。三家的实例参数含义不同,只有落到代码里才能做 code review、才能复现、才能在新区域快速克隆。
// AWS RDS MySQL:多可用区 + gp3 + 删除保护
resource "aws_db_instance" "orders" {
identifier = "orders-prod"
engine = "mysql"
engine_version = "8.0"
instance_class = "db.r6g.xlarge"
allocated_storage = 2000
max_allocated_storage = 4000
storage_type = "gp3"
multi_az = true
backup_retention_period = 14
backup_window = "18:00-19:00"
deletion_protection = true
performance_insights_enabled = true
storage_encrypted = true
}
// 阿里云 RDS MySQL:高可用版(主备跨可用区)
resource "alicloud_db_instance" "orders" {
engine = "MySQL"
engine_version = "8.0"
instance_type = "mysql.x4.xlarge.2c"
instance_storage = "2000"
category = "HighAvailability"
security_ips = ["10.0.0.0/8"]
instance_name = "orders-prod"
}
// 腾讯云 TencentDB for MySQL:双节点 + 8.0
resource "tencentcloud_mysql_instance" "orders" {
internet_service = 0
engine_version = "8.0"
charge_type = "POSTPAID"
mem_size = 32000
volume_size = 2000
instance_name = "orders-prod"
mysql_version = "8.0"
availability_zone = "ap-singapore-2"
instance_type = "CUSTOM"
root_password = var.mysql_root_password
}
四点评注:
- 参数名以 provider 版本为准。三家 Terraform provider 的属性会随版本变化,把
Version固定在 lock 文件里,别用「最新」。 - 别把
multi_az当成跨区容灾。它只解决单可用区故障,跨区要另建跨地域副本资源。 deletion_protection必须开。托管数据库的一次误删,恢复成本远高于一台服务器。- 密码走变量 + 密钥管理,不要进代码仓库。三家都支持从 KMS / 密钥管理服务取值。
一个实践建议:把「实例定义」「只读副本定义」「参数模板」拆成三个模块。因为只读副本的数量是随业务变化的,而主实例的规格相对稳定——混在一个模块里,每次加副本都要 touch 主实例的 state。
十二、迁移与割接:五步与一张检查表
从自建或跨云迁移,真正花时间的不是搬数据,而是校验与回滚预案。
| 步骤 | 做什么 | 工具(示例) | 验收标准 |
|---|---|---|---|
| 1. 评估 | 版本、字符集、SQL 方言、对象数量 | 慢查询日志 Top 200 + EXPLAIN 对比 | 每条 SQL 在新实例上执行计划可接受 |
| 2. 结构迁移 | 表、索引、视图、存储过程、触发器 | mysqldump / 厂商 DTS 结构迁移 | 对象数量与定义完全一致 |
| 3. 全量 + 增量 | 全量拷贝 + binlog 实时追平 | DTS / DMS / 自建 CDC | 增量延迟稳定在秒级 |
| 4. 数据校验 | 行数、关键列校验和、抽样比对 | pt-table-checksum / 厂商内置校验 | 差异为 0 或已逐条解释 |
| 5. 割接与回滚 | 停写、切域名、验证、观察 | DNS / 连接串 / 代理配置 | 首笔写成功,错误率回基线 |
割接窗口的八项检查:
- 应用侧连接串与连接池上限已按新实例调整(最容易漏的一项)。
- 只读流量已切到新实例的只读地址,且延迟监控已就位。
- 定时任务、离线作业、报表系统全部在清单里(它们往往直连旧库)。
- 备份与 PITR 已在新实例上验证过一次真实恢复。
- 回滚路径明确,且回滚所需的旧库仍可写(不要提前下线)。
- 监控告警的阈值按新实例重新校准(CPU 基线不同)。
- 慢查询日志、审计日志已开启并接入日志平台。
- 有一个人专门盯错误率曲线,不做其他事。
第 1 项值得单独强调:迁移事故里有相当比例不是数据库的问题,而是某个服务还用着旧的连接池大小,新实例的连接上限更低,于是一上线就打满。
十三、排障清单:按现象定位,而不是按经验猜
数据库故障的表象高度相似,根因却分布在不同层。下面这张表按「你看到的现象」组织,第一列就是值班时最该先看的判据。
| 现象 | 第一判据 | 常见根因 | 下一步 |
|---|---|---|---|
| CPU 100%,QPS 没涨 | 慢查询数、活跃连接数 | 缺索引、执行计划退化、统计信息过期 | 抓 Top SQL,EXPLAIN 对比 |
| QPS 下降,CPU 不高 | 连接数、锁等待 | 连接池打满、行锁排队、代理层限流 | 看 PROCESSLIST 的 State 分布 |
| 只读查询报错或结果旧 | 复制延迟、Seconds_Behind_Source | 大事务、DDL、备库规格不足 | 查 worker 回放进度 |
| 写入突然变慢,偶发超时 | 磁盘 IOPS、突发积分余额 | gp3 积分耗尽、存储打满 | 看 CloudWatch / 监控的 BurstBalance |
| 主备切换后应用不恢复 | DNS TTL、连接池旧连接 | 重连段未处理 | 缩短 TTL、开 keepalive |
| 连接数缓慢上涨 | 按来源 IP 聚合的空闲连接 | 连接泄漏、maxLifetime 未设 | 查 Sleep 连接归属 |
| 内存持续上涨后 OOM | buffer pool 外的会话缓冲 | sort_buffer / join_buffer 过大 × 高并发 | 降会话缓冲、限并发 |
| 凌晨固定时间抖动 | 备份窗口、批处理任务 | 自动备份抢占 I/O、定时任务全表扫描 | 错峰,或换低峰窗口 |
| 单表写入越来越慢 | 索引数量、二级索引写放大 | 索引过多、主键非递增 | 精简索引,改用递增主键 |
| 跨可用区延迟升高 | 应用与数据库的 AZ 分布 | 跨 AZ 调用、跨区读写 | 应用与主库同 AZ |
| 账单突然翻倍 | 存储增长、I/O 请求数、副本数 | 数据未归档、日志全开 | 按 10.2 的四个隐藏项核对 |
| 迁移后偶发重复数据 | 唯一索引与排序规则 | 排序规则不一致导致判重失效 | 对比两端 collation |
排障的第一原则是先分层再定位:连接层(能否连上)→ 执行层(SQL 是否变慢)→ 存储层(I/O 是否饱和)→ 复制层(备库是否追平)。跳过任何一层都会导致误判,最常见的误判是把「连接池打满」当成「数据库性能不足」,于是加规格,问题依旧。
十四、90 天落地路线
| 阶段 | 时间 | 交付物 | 验收标准 |
|---|---|---|---|
| 现状盘点 | 第 1–2 周 | 实例清单、规格、峰值指标、慢查询 Top 100 | 每个实例都有明确的业务归属人 |
| 基线压测 | 第 3–4 周 | 各引擎族的拐点曲线、复制延迟曲线 | 知道单实例的上限在哪 |
| 高可用补齐 | 第 5–6 周 | 多可用区 / 三节点选型、切换演练报告 | 演练 RTO 与错误率恢复时间有记录 |
| 读写分离 | 第 7–8 周 | 只读地址、会话粘滞策略、一致性级别 | 读流量占比与主库压力下降可量化 |
| 连接层治理 | 第 9 周 | 代理或连接池统一配置 | 弹性伸缩时连接数不再打满 |
| 成本优化 | 第 10 周 | 右规格化清单、预留采购建议 | 月度账单下降且有据可查 |
| 分片评估 | 第 11–12 周 | 分片必要性结论(做或不做,都要有数据) | 用 6.2 的判据表逐项打勾 |
这张表里最重要的是最后一行:「不做分片」也是一个需要数据支撑的结论。没有评估就上线,等于把架构风险留给半年后的自己。
十五、总结
关系型数据库的多云架构,说到底只有四个变量:引擎族决定天花板,拓扑决定故障形态,扩展方式决定复杂度,连接模型决定稳定性。四个变量里,前两个花钱能解决,后两个只能靠设计。
三条最值得记住的判断:
- 半同步不等于 RPO=0。超时退化是静默的,要监控退化事件而不是开关状态。
- 只读副本的语义在三族里不同。共享存储版的副本共享数据、只回放日志;自建兼容版的副本异步复制 binlog。把两者的延迟预期互换,是跨云架构里最常见的错。
- RTO 的重连段通常比切换段更贵。优化应用侧的重连与 DNS,往往比升级数据库拓扑更划算。
最后一句话:数据库是整套架构里迁移成本最高、回滚成本最高的组件。宁可多花两周做压测、切换演练和分片必要性评估,也不要在业务翻倍时被迫做一次全量重构。
🚀 企业出海需要云架构咨询?通过 7.chengzicloud.cloud 联系我们,获取专属方案。覆盖多云数据库选型、高可用与容灾设计、迁移割接与成本优化。
相关阅读
- 多云数据库跨云同步实战 — 本文第七节提到的复制管道,在那篇里有完整的 DTS/DMS/CDC 落地细节
- 多云缓存架构与多级一致性 — 减轻源库读压力的第一道防线
- 多云备份与勒索软件防护 3-2-1-1-0 — 本文谈可用性,备份与恢复点交给这篇
- 多云单元化架构与韧性设计 — 数据按单元切分之前,先定故障域边界
- 企业出海多云多租户隔离架构 — 租户隔离在数据库层落到实例、库、行三级
- 多云托管文件存储架构 — 非结构化数据的那一层,与本文正交
- Kubernetes 成本治理 Kubecost/OpenCost — 容器侧成本口径,与本文的数据库 TCO 互为补充
本文由 7.chengzicloud.cloud 提供,点击访问首页了解更多
常见问题(FAQ)
Q1:中小团队有必要一开始就上多可用区吗?
如果是生产库,有必要。多可用区把「单可用区故障」从一次几小时的事故,压缩成一次几十秒的切换,成本大约是单实例的两倍。对一年营收远高于这个差价的业务,它是最便宜的保险。只有内部工具、可重建的测试库,才适合单实例 + 快照。
Q2:共享存储版(Aurora / PolarDB / TDSQL-C)是不是一定比自建兼容版好?
不是。它的优势在读扩展快、副本延迟低、故障切换快、不用为高可用付第二份计算费;劣势在写入仍然单节点、I/O 按请求计费导致写入密集场景成本不可预测、以及部分运维细节(参数、插件、内核版本)与标准 MySQL 有差异。读多写少的业务收益明显,写入极高且小事务密集的业务反而可能更贵。
Q3:开了半同步复制,是不是就一定 RPO=0?
不是。半同步在等待超时后会退化为异步,而退化通常是静默的。MySQL 的默认超时是 10 秒,这意味着备库抖动超过 10 秒的那段时间里,主库已在接受「没有副本确认」的写入。要做到 RPO=0,需要多数派提交(三节点 / 强同步)或组复制,并且监控退化事件。
Q4:只读副本越多,读性能就越好吗?
通常会变差。副本多了,写入的 binlog 分发压力、跨区流量、监控与管理成本都上升,而每个副本的命中率并不会因为多一个副本而提高。正确的顺序是:先加缓存、再把检索类查询移到搜索引擎、最后才加只读副本。加副本前先确认读流量里有多少是可以被缓存掉的。
Q5:什么时候必须分库分表?
用 6.2 的判据表判断,比较硬的信号是:单表行数进入亿级、单库存储超过 2–4 TB、写入 QPS 长期超过单实例上限的 60%、单表 DDL 需要停机窗口。注意「行数」不是唯一信号,一张大表但只按主键查,往往比一张小表但写入频繁更健康。
Q6:分片键选错了还能救吗?
能,但代价很高。补救手段有三种:加一层映射表(把查询键翻译成实际分片)、做数据重分布(停机或双写迁移)、或者改成分布式数据库让中间件接管路由。第三种最省事但会绑定厂商。所以分片键要在设计阶段用「未来的查询模式」验证,而不是只按当前 SQL 验证。
Q7:跨云双活双写值得做吗?
对数据库这一层,绝大多数业务不值得。双写要解决冲突检测、自增冲突、跨云事务和锁持有时间放大四个问题,任何一个都可能吃掉收益。更务实的是「单写多读 + 明确的切换决策点」:写只落一个云,另一个云提供读与灾备接管,切换是单向且有预案的。
Q8:迁移后应用连不上,最先查什么?
按顺序查三件事:安全组 / 白名单是否放行了应用所在网段(跨云时最容易漏)、实例的连接数上限是否低于应用连接池总和、以及 DNS 是否已生效(TTL 未过期会连到旧实例)。这三项覆盖了迁移后连接问题的绝大部分。
Q9:为什么压测时性能很好,上线三天就变慢?
通常是三个原因之一:压测数据量小于内存,缓存全命中;存储的突发积分(如 gp3 的 BurstBalance)在持续负载下耗尽,IOPS 掉回基准值;或者真实流量的分布有热点,压测的均匀分布测不出热点行锁。对应解法是加大压测数据量、盯住突发积分、用真实流量回放做一次验证。
Q10:预留实例 / 节省计划能省多少,值得买吗?
长期稳定的主库通常能省三成左右的按需费用,一年期、全预付折扣最大。判断是否值得买的依据是「这个实例未来 12 个月会不会被降规格或迁移」——如果半年内要做架构调整,买了预留反而被锁住。稳妥做法是先右规格化,再对稳定部分买预留。
Q11:本文这套方法跟 10-02 单元化、10-21 多租户怎么配合?
顺序是:先定单元与租户边界(10-02、10-21),再按边界决定每个单元用哪种数据库拓扑(本文),最后才是缓存(10-09)与备份(09-11)。反过来先选数据库产品再想边界,通常会在租户数上来之后被迫做一次数据搬迁。
Q12:三云的托管数据库,能不能用同一套监控?
指标名不同,但语义可以对齐。建议统一采集五类指标:连接数、QPS/TPS、P99 延迟、复制延迟、存储与 IOPS 使用率。把这五项映射到同一套看板与告警规则上,跨云切换时运维习惯不用重学。用各家的 API 或 Prometheus exporter 都能做到,成本不高但收益长期。