TDSQL分布式数据库架构解析:单机瓶颈、分片键与扩容落地
三年前接手过一个计费系统的重构,流水表从两亿行涨到九亿行,单表一点二 TB,运维同学每周都要跑一次大表归档,稍微复杂一点的统计查询一上去,主库 IO 直接打满,连带在线扣费接口的 P99 从 30ms 飙到 800ms。加从库、加索引、换更高规格的机器、把冷数据拆到历史库,能试的都试了,效果当然有,但本质上都是把墙往后推两米,半年后照样撞上去。后来我们把整套流水拆到腾讯云数据库 TDSQL 上,才算把"容量"这件事从日常焦虑清单里划掉。这篇就借着这次迁移,把 TDSQL 这套企业级分布式数据库的产品轮廓、架构骨架、分片设计、一致性取舍和落地流程,按我实际踩过的顺序讲一遍,尽量说人话,也尽量说清"为什么这么设计"。如果你正被单机数据库的容量或者并发顶着走,或者在做金融、政务、游戏这类对数据可靠性要求高的系统选型,这篇应该能帮你少走几段弯路。
1. 单机 MySQL 顶到墙之后:TDSQL 到底在解哪类题
先把预期对齐。分布式数据库不是"更快更强的 MySQL",它是用一套更复杂的架构,换来解决单机数据库从原理上就解决不了的问题。搞不清这一点,选型阶段就容易跑偏,最后变成"为了分布式而分布式",运维复杂度上去了,收益却没多少。
1.1 单机数据库的四道墙
第一道墙是单机存储容量。一台物理机的本地盘容量是有上限的,SSD 单盘常见也就几 TB 到十几 TB,考虑 RAID 和副本冗余之后能用的更少。业务数据是无上限增长的,这两条线迟早会交叉。
第二道墙是单机写入吞吐。MySQL 的写入最终要落到 redo log 和 binlog 上,还要保证顺序写,单实例的写入能力大致就在几万 TPS 这个量级。再往上堆 CPU 核数,收益会迅速衰减,因为锁竞争、刷盘、组提交都会变成瓶颈。
第三道墙是连接数与线程模型。MySQL 是 one-connection-one-thread 的模型,几千个活跃连接就会出现明显的上下文切换开销。分库分表中间件能解决路由问题,但连接池、会话变量、事务边界这些麻烦还是得应用层自己扛。
第四道墙是DDL 与运维窗口。一张几十亿行的表加个字段,即使有 Online DDL,也可能跑几个小时,期间还得担心主从延迟和锁等待。备份恢复的时间窗口更是随数据量线性增长,出了事恢复一次可能要按天算。
这四道墙的共同点是:它们都不是"调优"能绕过去的,而是单机架构的物理边界。分布式数据库的出发点,就是把这几件事从"单机能力"问题,转化成"架构设计"问题。
1.2 分布式数据库的两种流派,TDSQL 站哪一边
市面上主流的分布式数据库大致分两派。一派是原生分布式,从存储层开始就重新设计,底层是共享的分布式存储或者自研的存储引擎,计算和存储彻底分离;另一派是分片式(Sharding),用成熟的关系型内核做单个数据节点,在上面加一层路由和协调,把多个节点拼成一个逻辑库。
TDSQL 的 MySQL 版属于后者,但做得比通常意义上的"中间件加分库"要深得多。它不是简单地在应用和 MySQL 之间插一个 Proxy,而是把接入层、数据节点、全局事务、元数据管理、运维管控都做成了产品的一部分。数据节点内部是经过深度改造的 MySQL 内核,SET 内部的主从复制支持强同步,跨 SET 的事务由全局事务管理器统一协调,元数据由专门的集群管理组件负责,扩容时的数据搬迁也是平台自动完成的,不需要 DBA 手工做数据同步。
这个技术路线的最大好处是兼容性成本低。因为底层就是关系型内核,SQL 语义、事务语义、锁行为都和原生数据库接近,应用改造主要集中在分片键和跨分片查询上,而不是把整个数据访问层重写一遍。对于一个已经跑了十年的业务系统来说,这个差别是能不能落地的关键。
1.3 什么时候该上 TDSQL,什么时候别上
我个人的判断标准比较粗暴,满足下面任意两条,就可以认真考虑分布式了:
- 单表数据量超过五亿行,或者单实例数据量超过两 TB,且增长曲线看不到拐点
- 峰值写入 TPS 持续超过单实例安全水位(通常是实测峰值的 60%)
- 业务需要按水平方向线性扩展读能力,且应用侧的读写分离已经做到头了
- 有明确的监管或者业务要求,需要多副本强一致、同城或异地容灾
- DDL 窗口、备份窗口已经严重挤压业务可用时间
反过来,如果只是慢查询多、索引没建好、连接池配置不合理,这些属于应用层和 SQL 层的问题,上分布式数据库不会自动变好,反而会因为跨分片查询让问题更显眼。分布式数据库放大了好的设计,也放大了坏的设计,这一点要有清醒认识。
企业级定位还体现在另外几个方面:一是金融级高可用,通常要求同城双活或者两地三中心;二是数据可靠性,RPO 要求接近零;三是可运维性,需要有完整的监控、审计、备份、灰度能力。TDSQL 的产品形态基本是围绕这三个诉求搭起来的,所以它在互联网高并发场景里能用,在银行核心、政企平台这类场景里也能用。
2. 拆开 TDSQL 的骨架:一条 SQL 从客户端到落盘的全过程
理解架构最好的方式,不是看组件图,而是跟着一条 SQL 走一遍。搞清楚每一步谁在处理、失败了会怎样,后面调优和排障才有抓手。
2.1 Proxy 接入层做了什么
应用连上 TDSQL 的第一个组件是 Proxy,它对外暴露的是 MySQL 协议端口。也就是说,应用侧用原生的 MySQL 驱动、原生的连接池,代码里基本感知不到 Proxy 的存在,这一点在迁移时非常省事。
Proxy 内部做的事情比想象中多:
- SQL 解析与改写。拿到 SQL 之后先做词法和语法分析,识别出表名、分片键、查询条件,判断这条 SQL 是单分片查询、跨分片查询还是全局查询。
- 路由计算。根据分片键的值和元数据里的分片规则,算出目标 SET 列表。如果查询条件里带了分片键等值条件,就能精准落到一个 SET;如果没带,就可能需要广播到所有 SET。
- 结果合并。跨分片的查询需要在 Proxy 层做归并,包括排序、聚合、分页、去重。这也是跨分片查询性能损耗的主要来源。
- 读写分离。写请求发给主库,读请求可以按策略发到只读副本,Proxy 层可以配置读权重和延迟阈值。
- 连接管理与会话保持。维护到后端各 SET 的连接池,同时保证事务内的会话上下文一致。
有一点需要特别注意:Proxy 是有状态的吗。在 TDSQL 的部署里,Proxy 通常是多节点部署的,前面挂负载均衡,单个 Proxy 故障不会导致整体不可用,但正在执行的长事务可能会断开。所以应用侧一定要有重试和事务补偿机制,尤其是那种跑几分钟的批量任务,别指望网络层帮你兜住。
2.2 SET:一个分片的内部构成
一个 SET 就是一个数据分片,内部结构是一个主库加若干个从库。默认情况下通常是一主两从,主库承担写和一部分读,从库承担只读流量和容灾备份。
SET 内部的复制方式决定了这套系统的数据可靠性上限。常见的有三种模式:
| 复制模式 | 主库提交条件 | RPO | 对写入延迟的影响 | 适用场景 |
|---|---|---|---|---|
| 异步复制 | 主库本地提交即可返回 | 可能丢数据 | 最低 | 纯读从库、离线分析 |
| 半同步复制 | 至少一个从库 ACK | 窗口内可能丢 | 中等 | 一般业务库 |
| 强同步复制 | 多数派从库落盘后提交 | 接近零 | 较高 | 金融、计费、账务 |
强同步模式下的写入路径会变长,因为主库要等从库确认,网络往返时间会直接叠加到事务提交延迟上。这就是为什么同城部署和异地部署的延迟表现差别巨大——跨地域的 RTT 通常在十几毫秒到几十毫秒,放到每一次事务提交上,累积起来非常可观。所以容灾方案里,强同步一般只在同城机房之间做,异地更多采用异步或者专门的同步通道。
2.3 全局事务管理器与分布式事务的提交路径
跨 SET 的事务是分布式数据库最难的部分。TDSQL 的做法是引入全局事务管理器(Transaction Coordinator),配合两阶段提交来保证一致性。
一条跨分片的事务,大致经历这几个阶段:
- 客户端开启事务,Proxy 从全局事务管理器申请一个全局事务 ID
- Proxy 把事务内的各条 SQL 分别路由到对应的 SET,各 SET 在本地开启子事务,并挂在这个全局事务 ID 下
- 提交时,先进入 prepare 阶段:全局事务管理器通知所有参与的 SET 做预提交,把 redo log 刷盘但不释放锁
- 所有 SET 都 prepare 成功之后,进入 commit 阶段:全局事务管理器写入提交点,然后通知各 SET 真正提交
- 如果任何一个 SET 在 prepare 阶段失败,全局事务管理器发起回滚,各 SET 回滚本地子事务
这个机制的关键在于提交点日志。只要全局事务管理器把"这件事要提交"这个决定落盘了,即使中途有节点宕机,恢复之后也能通过重放日志把事务推到最终状态。
实操上有一条经验必须记牢:跨分片事务的延迟明显高于单分片事务,因为它多了一轮协调和两次网络往返。如果你的业务里有大量高频的跨分片写,那大概率是分片键设计出了问题,而不是分布式的锅。我们那次迁移完之后做过统计,跨分片事务占比从初期的 18% 压到 3% 以下,整体写入延迟才回到可接受区间。
2.4 全局唯一的自增 ID 从哪来
分库分表之后 AUTO_INCREMENT 就不再全局唯一了,这是所有分片方案都要面对的同一个问题。TDSQL 提供了全局序列的能力,由平台统一分配,应用可以像用普通自增列一样拿到全局递增的 ID。
除此之外,实际项目里还有几种常见的替代方案,可以按业务特点选用:
- 号段模式:从序列服务批量取一段 ID 缓存在本地,用完再取。性能最好,但服务重启可能浪费一段;适合对连续性没要求的高并发写入。
- Snowflake 类算法:本地生成,无网络开销,但依赖时钟回拨处理;适合超大规模写入。
- 业务自定义:比如订单号用"日期 + 分片号 + 序列"拼接,可读性好,也方便定位数据落在哪个分片。
选哪种,取决于你是否需要 ID 严格连续、是否能接受空洞,以及是否有按 ID 反查分片的诉求。
3. 分片键选错,后面全是补丁
如果让我只给一条分布式数据库的落地建议,那就是:把分片键的选择当成一次架构评审来做。这件事一旦定下来,改的成本极高,因为改分片键本质上等于全量数据重分布。
3.1 分片键同时决定了路由、均衡和扩展性
分片键的作用有三个,而且是互相牵制的。
第一是路由效率。 带分片键等值条件的查询能精准落一个 SET,不带就得广播到全部 SET。广播查询在分片数少的时候还能忍,一旦扩到几十上百个分片,单条查询可能触发上百次后端请求,Proxy 的合并开销也会同步放大。
第二是数据均衡。 分片键的取值分布直接决定了数据在各 SET 上的分布。用自增 ID 分片,新数据永远落在最后一个分片,形成写入热点;用时间字段分片,则会出现"历史分片冷、当前分片烫"的典型形态。
第三是扩展性。 扩容时是否需要大量跨分片数据搬迁,取决于分片键的哈希方式和分片数量能否整除。用哈希分片的话,从 8 个分片扩到 16 个,理论上能做到只搬一半数据;扩到 12 个的话,几乎全量重分布。
3.2 三种常见分片策略及其代价
| 策略 | 优点 | 代价 | 典型适用 |
|---|---|---|---|
| 哈希分片(HASH) | 数据分布均匀,无热点 | 范围查询必须广播 | 用户维度、订单维度的高并发点查 |
| 范围分片(RANGE) | 范围查询友好,易归档 | 容易产生写入热点 | 日志、流水、按时间归档的场景 |
| 列表分片(LIST) | 可按业务属性隔离 | 分布容易不均,扩展不灵活 | 多租户、多region 数据隔离 |
实际项目里最常见的做法是组合分片:用业务主体 ID(比如商户号、用户号)做哈希分片保证均衡,同时在表内保留时间字段做分区或者索引,兼顾范围查询。
还有一种情况值得单独提:多对多关系表。这类表往往既需要按 A 维度查,也需要按 B 维度查,不管怎么选分片键,总有一半查询要广播。常见的处理办法是建两张冗余表,分别按 A 和 B 分片,写入时双写,读取时各走各的。听起来不优雅,但在实际系统里,用空间换查询效率往往是最务实的方案。
3.3 选分片键的几条硬规则
我在几个项目里总结出来的判断顺序是这样的:
- 优先选高频查询条件里出现频率最高的那个字段。分片键的第一职责是让大多数查询走单分片。
- 必须是高基数字段。性别、状态这种低基数字段做分片键,等于把数据强行压到几个分片里。基数至少要到分片数的十倍以上才比较稳。
- 取值要足够均匀。如果某个值天然占比超过 20%,那它就是热点,无论怎么哈希都救不回来。
- 尽量不可变。分片键一旦更新,就意味着这条记录要在两个分片之间搬迁,成本极高,很多分布式数据库索性直接禁止更新分片键。
- 避免用时间字段单独做分片键,除非业务本身就是归档型写入,且能接受写入热点。
提示:分片键字段的业务含义要经得起推敲。我见过一个项目用"创建人 ID"做订单表的分片键,理由是这个字段基数高、分布均匀,结果上线后发现按订单号查订单成了广播查询,而按订单号查恰恰是最核心的接口。
3.4 扩容搬迁:数据怎么搬,业务会不会抖
分片数量不可能一次规划到位。业务量涨了要扩,某些分片数据偏多要重平衡,这些都是常规操作。
TDSQL 的水平扩容通常走这几步:先在管控台申请新规格,平台自动创建新的 SET;然后按分片规则计算出哪些数据需要迁移;接着在源和目标之间建立同步通道,做全量加增量的数据搬迁;数据追平之后,在元数据里切换路由规则,把新分片纳入服务;最后清理源端的冗余数据。
这里有几个容易被忽略的细节:
- 搬迁期间业务是双写还是单写。不同实现不一样,如果搬迁期间源分片仍在接受写入,需要增量同步把变更追平,同步延迟就是切换窗口的决定因素。
- 切换瞬间的连接抖动。路由规则变更后,Proxy 需要刷新元数据缓存,应用侧的长连接可能会遇到短暂的报错,所以扩容最好安排在低峰期。
- 搬迁期间的性能损耗。数据搬迁本身会占用磁盘 IO 和网络带宽,如果磁盘水位本来就高,容易雪上加霜。扩容前先把磁盘水位压到 70% 以下。
4. 一致性、可用性与容灾:上线前必须敲定的几个开关
这一节讲的是那些"上线之后再改代价极大"的参数。它们的共同特点是:都涉及一致性、可用性和性能三者的取舍,没有免费午餐,只能根据业务性质做选择。
4.1 强同步、半同步、异步的取舍
前面表格里已经对比过三种复制模式。这里补充几个实际决策时容易被忽略的点。
强同步不等于零延迟。 强同步只保证数据在多数派落盘之前主库不返回成功,它不保证从库的读一致性。如果业务需要"写完之后立刻读到",要么强制读主库,要么使用一致性读的能力,让 Proxy 根据事务位点去选择已经追平的从库。
强同步会放大网络抖动的影响。 一次网络抖动的后果不只是慢,还可能触发主库降级或者切换。所以强同步一般只在同城低延迟网络里用,跨地域慎用。
不同分片可以配不同的模式。 这是分片式架构的一个隐藏优势。核心账务表配强同步,日志流水表配异步,既能守住关键数据的可靠性,又不至于让所有写入都背上强同步的延迟成本。
4.2 两地三中心与跨地域数据同步
企业级场景里,容灾等级通常按"同城双活 + 异地灾备"来设计。同城两个机房之间距离近,网络延迟低,可以做强同步,实现接近零的 RPO;异地机房距离远,一般通过专门的同步通道做异步复制,RPO 会有一个秒级到分钟级的窗口。
跨地域同步的关键在于冲突处理。如果两侧都能写,就必须有冲突解决策略,比如按时间戳、按优先级、或者干脆约定某一侧只读。金融场景里更常见的做法是主备模式:平时一侧承担全部写入,另一侧只做只读和灾备,切换时人工或者半自动发起。
还有一点是数据一致性的验证。异地同步链路最怕"看起来是同步的,实际上已经断了两天"。必须要有独立的对账机制,定时抽样比对两侧的位点和关键表的行数、校验和,而不是只依赖同步组件自己报的状态。
4.3 主备切换:触发条件、切换时长与演练
自动切换的触发条件通常包括主库进程异常、网络不可达、复制链路中断超过阈值等。切换过程一般由集群管理组件判定,选择复制位点最新的从库提升为新主库,然后更新路由。
有两个数字要提前问清楚:切换判定时间和切换完成时间。前者是"多久没心跳就认为主库挂了",设得太短容易误切,设得太长则不可用时间拉长;后者是"新主库提升并对外服务需要多久",包含等待复制追平、重新挂载 VIP 等步骤。加起来才是业务真正感受到的中断时长。
演练这件事,我的态度是必须做,而且要真做。常见的做法是每个月在预发环境模拟一次主库宕机,观察业务侧的报错率、重试成功率、恢复时间。很多隐藏问题都是演练暴露出来的,比如应用侧连接池没有做失效重连、重试逻辑没有做幂等、监控告警没有覆盖切换事件本身。
5. 兼容性清单:MySQL 与 Oracle 两种口味的边界在哪
迁移项目里,兼容性清单往往比架构设计更耗时,因为它涉及一行行 SQL、一个个存储过程。提前把边界摸清楚,能省下大量返工。
5.1 MySQL 兼容模式的真实覆盖度
TDSQL 的 MySQL 版对原生协议的兼容度是比较高的。应用侧基本可以做到:
- 使用原生 MySQL 驱动和连接池,不需要换 SDK
- 沿用原有的建表语句、索引定义、字符集设置
- 大部分 SQL 语法、内置函数、聚合函数可以直接用
- 事务隔离级别沿用 MySQL 的语义,习惯了 RR 的团队不用重新理解一遍
但有几点要提前确认,这些是最常出问题的地方:
- 跨分片 JOIN 的性能。语法上可能支持,但执行计划会变成每个分片各查一遍然后再归并,数据量大的时候非常慢。原则上跨分片 JOIN 应该改成在应用层组装,或者通过冗余字段把关联关系落到同一个分片内。
- 跨分片聚合。
SUM、COUNT、GROUP BY在跨分片时需要在 Proxy 层合并,分页尤其麻烦,LIMIT的偏移量越大代价越高。 - 子查询和复杂执行计划。部分复杂子查询的执行路径可能与原生 MySQL 不同,需要通过执行计划确认。
- DDL 的传播。在分布式环境下,加字段、加索引要在所有分片上执行,虽然平台会统一调度,但耗时会比单机长。
5.2 Oracle 兼容模式省下的改造量
大量传统行业的系统是从 Oracle 迁过来的,代码里塞满了 PL/SQL、存储过程、包、序列、同义词、DBLink。如果逐个重写,工作量可能是按人年算的。TDSQL 提供的 Oracle 兼容模式,目标就是把这部分改造量压下来。
它主要覆盖这些方向:
- 数据类型映射:
NUMBER、VARCHAR2、DATE、CLOB等常见类型可以直接对应 - PL/SQL 支持:存储过程、函数、触发器、包的语法尽量对齐
- 常用内置包和函数:
TO_DATE、NVL、DECODE、ROWNUM这类高频函数 - 对象体系:序列、同义词、DBLink 等
实际迁移时的建议是:先做一轮自动化扫描,把不兼容的语法和对象列出来,按调用频率排序,优先处理高频路径。低频的、只在年报或者月底跑的存储过程,可以放到二期处理,用应用层逻辑替代。
5.3 必须改写的几类 SQL 和对象
不管兼容度多高,下面这几类基本都得改:
| 问题类型 | 典型写法 | 改写思路 |
|---|---|---|
| 无分片键的查询 | where status = 1 |
补分片键条件,或建索引表/ES 辅助查询 |
| 跨分片 JOIN | 大表关联大表 | 应用层拼装,或按同一分片键冗余 |
| 跨分片排序分页 | order by time limit 100000, 20 |
改成基于游标(上一页最大 ID)翻页 |
| 全局唯一约束 | 唯一索引跨分片 | 用全局序列或独立的去重服务 |
| 大事务 | 一次更新几十万行 | 拆成小批次循环提交 |
| 分布式锁 | 表级锁语义 | 改用分布式锁组件 |
注意:大事务在分布式环境下代价被显著放大,因为它会长时间持有多个分片的锁,还可能拖慢全局事务管理器的吞吐。经验值是把单次事务影响的记录数控制在几百到几千行以内。
6. 落地流程:从容量估算到灰度上线
讲完架构和设计,说点真正动手的部分。这一节按时间顺序走一遍完整的落地流程。
6.1 容量与规格估算怎么做
容量估算最忌讳拍脑袋。我一般按下面这套流程走:
第一步,算数据量。 拿当前单表行数乘以平均行长,再乘上未来两年的增长率(通常按业务增长曲线外推,别直接用 100%)。同时把索引体积算进去,索引通常占数据体积的 20% 到 50%。
第二步,算分片数。 目标是让每个分片的数据量控制在合理区间。经验上单分片数据量控制在几百 GB 以内比较好,这样备份、恢复、DDL 都在可控范围。假设总量 8TB、索引占 40%,那就是约 11TB,按每分片 500GB 算,需要 22 个分片,向上取整并留冗余,取 24 或 32 个。
第三步,算规格。 用峰值 QPS 除以单分片的安全承载能力。单个 SET 的承载能力要按实测来,不能按宣传值。压测的时候要看清楚是单分片性能还是整体性能。
第四步,留冗余。 CPU 和内存留 30% 到 40% 的余量,磁盘留 40% 以上。分布式系统里节点多,任何一个节点成为瓶颈都可能拖慢整体。
6.2 表结构与 SQL 改造清单
这部分是工作量最大的。我的做法是列一个表,把所有涉及的表和 SQL 分三类:
- A 类:可以原样迁移。带分片键的点查、小范围范围查、单分片内的事务。这类占大多数,改造量小。
- B 类:需要加条件或改写法。不带分片键的查询、跨分片 JOIN、深分页。这类需要改 SQL,或者加辅助索引。
- C 类:需要架构级调整。全局唯一约束、大事务、跨分片强一致的统计报表、分布式锁。这类要重新设计。
改造顺序上,建议先做 B 类,因为它能立刻缓解跨分片查询;C 类放到二期,因为它通常需要上下游一起改。
另外有个细节很值得做:给每张表标注分片键,并且在代码评审里把"是否带分片键"作为一条硬性的检查项。我见过太多项目,架构阶段分片键定得好好的,半年后新来的同学随手加了一个不带分片键的查询,把整体性能拖下去。
6.3 压测、灰度与回滚预案
压测不只是跑个基准值,重点在验证这几件事:
- 跨分片查询的比例。这个数字直接决定了整体性能。目标是把跨分片查询占比压到 5% 以内。
- 热点分片。看各个 SET 的 QPS 和 IO 分布是否均匀,不均匀说明分片键有问题。
- 扩容行为。在压测环境模拟一次扩容,看业务侧是否出现报错和延迟抖动。
- 故障切换。手动杀主库进程,观察切换时间和业务恢复情况。
灰度上线建议按写少读多的表先上、核心交易表后上的顺序推进,同时准备好双写和数据校验工具。回滚预案至少要包括:数据反向同步方案、应用侧版本回退方案、以及数据一致性校验脚本。这三点缺一不可,真出事的时候没时间现写。
7. 运维视角:监控指标、热点定位与数据误删处理
上线只是开始,后面是长期的运维。分布式数据库的运维复杂度确实比单机高,但只要把监控和排查路径固定下来,日常并不会太累。
7.1 必须盯住的几组指标
| 指标类别 | 具体指标 | 关注点 |
|---|---|---|
| 接入层 | Proxy QPS、连接数、慢查询数、错误码分布 | 错误码突然变化通常意味着后端异常 |
| 计算节点 | CPU、内存、活跃连接、锁等待 | 单节点 CPU 明显高于其他,说明热点 |
| 存储与 IO | 磁盘使用率、IO 利用率、IO 等待 | 磁盘超过 80% 要立刻处理 |
| 复制链路 | 主从延迟、复制状态、同步模式 | 延迟突增往往先于故障发生 |
| 事务 | 全局事务数、跨分片事务占比、事务耗时分布 | 跨分片占比上升是性能退化的先行指标 |
| 元数据 | 路由版本、节点状态、切换事件 | 切换事件要接通告警 |
监控的关键不是指标多,而是能定位到分片粒度。整体 QPS 正常不代表没问题,某个分片可能已经在满载运行了。所以看板一定要能下钻到单个 SET。
7.2 慢查询与热点分片的定位链路
遇到性能问题,我习惯按这个顺序排查:
第一层,看整体。 是不是所有分片都慢,还是只有部分慢。全都慢,通常是接入层或者网络问题;部分慢,是热点问题。
第二层,看慢查询日志。 按耗时和扫描行数排序,找出 TOP 10。重点关注两类:一类是没带分片键的广播查询,一类是扫描行数极大的查询。
第三层,看执行计划。 确认索引是否被正确使用。分布式环境下的执行计划会比单机复杂,因为多了路由和合并两个阶段。
第四层,看分片分布。 统计各分片的 QPS、数据量、连接数。如果某个分片的数据量是其他分片的两倍,那就是分片键的分布问题,需要考虑重平衡。
第五层,看上游。 有时候问题不在数据库,而在应用侧的连接池配置、批量提交策略、或者某个新上线的定时任务。我遇到过一次线上抖动,最后查出来是一个统计任务在凌晨全表扫描,而这个任务在代码review时被当成"反正是凌晨跑"给放过去了。
7.3 备份恢复与数据误操作兜底
分布式数据库的备份比单机复杂,因为要保证多个节点之间的备份是同一个一致性位点。一般产品化程度高的平台会提供全量加增量的备份策略,并且支持按时间点恢复。
运维上要提前确认这几件事:
- 备份窗口和耗时。数据量大了之后,全量备份可能跑好几个小时,要确认这个窗口业务能不能接受。
- 恢复的粒度。是整实例恢复,还是单库单表恢复,还是可以恢复到某个时间点。粒度越细,恢复的灵活性越高,但实现复杂度也越高。
- 恢复演练。备份没验证过等于没有备份。建议每季度做一次真实的恢复演练,用一个隔离环境把备份还原出来,确认数据完整可用。
至于误删数据,除了依赖备份恢复,还有两个常用手段:一是回收站或者闪回能力,短时间内可以快速找回;二是延迟从库,专门保留一个延迟若干小时的副本,用来对冲误操作。后者在分布式环境下同样适用,而且成本不高,值得配一个。
8. 选型对比:TDSQL、TDSQL-C 与自建 MySQL 怎么选
最后一个实际问题:同样是腾讯云数据库,TDSQL 和 TDSQL-C 到底怎么选,什么情况下还不如自建。
8.1 三种形态的定位差异
**TDSQL(分布式版)**的核心价值是水平扩展和金融级高可用。它适合数据量大、并发高、对可靠性要求苛刻的场景,代价是架构复杂度和运维成本更高。
**TDSQL-C(云原生版)**的特点是计算存储分离,存储层是共享的分布式存储,因此扩容快、按量计费更灵活、单实例能支撑比传统架构更大的存储容量。它适合需要弹性伸缩、但数据规模还没到必须分片的业务。
自建 MySQL的优势是掌控力强、没有平台绑定,劣势是运维成本高,高可用、备份、监控、容灾都要自己搭,而且很多问题只有在真出事的时候才暴露。
三种形态的选择逻辑大致是这样:
| 判断维度 | 优先 TDSQL 分布式版 | 优先 TDSQL-C | 考虑自建 |
|---|---|---|---|
| 数据量 | 数 TB 以上且持续增长 | 几百 GB 到数 TB | 小规模或特殊需求 |
| 写入并发 | 单机写入已到瓶颈 | 中等偏高 | 低 |
| 容灾要求 | 同城双活、两地三中心 | 单地域多可用区 | 无强要求 |
| 弹性需求 | 需要按分片扩容 | 需要快速扩缩容 | 固定容量 |
| 团队能力 | 有 DBA 团队支撑 | 有基础运维能力 | 有较强自研能力 |
8.2 成本账怎么算
成本的对比不能只看实例价格,要把这几项都算进去:
- 实例和存储费用:分布式版因为节点多,实例费用通常更高,但单位数据的成本可能更低。
- 人力成本:这是最容易被低估的一项。自建一套具备高可用、备份、监控、容灾的 MySQL 集群,至少需要半个到一个专职 DBA。
- 故障成本:一次数据丢失或者长时间不可用造成的业务损失,往往远超几年的数据库费用。这一项虽然算不出精确数字,但在决策时必须考虑。
- 迁移和改造成本:分布式数据库需要应用改造,这部分投入要在项目立项时就算清楚。
8.3 一个务实的迁移顺序
如果不想一次跳太大,我的建议是分三步走:
第一步,把自建 MySQL 迁到云上的托管 MySQL 或者云原生版本。 这一步主要是把运维负担降下来,让团队先熟悉云上的监控、备份、告警体系,应用侧基本不用改。
第二步,做分片键设计和应用改造,在一个非核心业务上试点分布式。 选一个数据量大但重要程度相对低的业务,比如日志、报表、历史订单,把分片键、SQL 改写、压测、灰度这套流程完整跑一遍。
第三步,核心业务分批迁入。 有了前面的经验,核心业务的迁移风险会小很多。这时候重点放在双写校验、灰度切流和回滚预案上。
我们现在这套系统就是按这个节奏走的。最深的感受是,分布式数据库带来的真正的复杂度不在数据库本身,而在**"约束变成显式的"**这件事上——以前你随手写一条不带条件的查询,性能差归差,能跑;现在它会明确地以广播查询、以热点分片的形式反馈给你。这其实是一件好事,它逼着团队把数据访问模式想清楚。我在实际操作中的体会是,凡是迁移过程踩到的坑,回头去看几乎都能在应用侧找到对应的设计缺陷,数据库只是把它提前暴露了出来。把这一点想通,落地过程会顺很多。