从ECS自建MySQL迁移到瑶池RDS:小应用上云全流程实战
2026/9/13 6:53:49 网站建设 项目流程

接到一个内部小应用的迁移需求:一套运行在 ECS 上的自建 MySQL,要迁到瑶池数据库 RDS。数据量不大,业务也不复杂,属于典型的小应用场景,但真上手做的时候发现,流程虽然标准,细节却一点都不少。我把自己这次从评估、迁移、校验到成本对比的完整过程写下来,希望能给正准备做同类迁移的同行提供一份“照着做就行”的参考。

先说背景。这是一套内部业务系统,前后端部署在一台 4 核 8G 的 ECS 上,MySQL 是 5.7,跑在同一个实例里,用 Docker 方式部署,数据量约 180GB,线上峰值 QPS 在 300 左右,没有从库,备份靠 crontab 每天凌晨执行 mysqldump,备份文件通过 ossutil 传到 OSS。这套架构初期没什么问题,但运行一年多后,三个问题越来越明显:磁盘空间告警频率变高,mysqldump 全量备份耗时从 20 分钟涨到 40 多分钟,偶尔大查询会把磁盘 IO 打满,导致应用接口超时。简单说,自建方案把“数据库稳定运行”这个压力全压在了一个人身上——升级要自己来,参数调优要自己来,出了问题还得半夜爬起来处理。所以在评估完业务现状后,我决定把数据库迁到瑶池数据库 RDS MySQL,把运维压力交出去,顺便重新梳理一下成本账。

1. 先说结论:我为什么决定从 ECS 自建 MySQL 迁到瑶池 RDS

1.1 从 ECS 自建 MySQL 的痛点,到底痛在哪

很多人觉得小应用用自建 MySQL 挺合适,毕竟数据量不大、并发不高,一台 ECS 就能带起来。这个说法在业务初期没问题,可一旦系统变成长期运行的生产业务,自建的隐性成本就显现了。

第一个痛点是备份。mysqldump 做逻辑备份,数据量上了百 GB 之后,从执行到完成往往要半小时以上,而且备份期间会对实例产生额外 IO 压力。我遇到过两次因为备份时间和业务高峰期重叠,导致慢查询明显增多的情况,后来只能把备份时间调到凌晨 3 点。哪怕是这样,也没法完全放心:如果某天磁盘满了,mysqldump 写不进去,那当天的备份就是失败的,而失败监控并不会主动报警。

第二个痛点是高可用。自建环境下要保证高可用,得自己搭主从复制、自己处理故障切换。小团队通常没有精力维护一套完整的 HA 方案,于是“可用性”就退化成“挂了之后快速恢复”。但数据库这种基础设施,恢复时间直接决定业务不可用时长,一旦数据文件损坏,恢复过程只会比想象中更复杂。

第三个痛点是版本升级和参数优化。MySQL 5.7 已经停更维护,继续跑在公网上本身就有安全风险。但升级到 8.0 并不是改个镜像版本那么简单:涉及字符集、认证插件、SQL 兼容性,还要做全量回归测试。这些事单独抽出来看都是小事,加在一起就是不小的工程。

1.2 瑶池数据库 RDS 解决了什么问题

瑶池数据库 RDS 是云上的托管数据库服务,简单理解就是:你不用再管服务器和数据库软件本身,只需要关注“库表结构、SQL 质量、数据内容”这些业务侧的事。对这个小应用来说,我拿到的最直接收益有三个:

  • 自动备份策略:可以配置每日自动备份加日志备份,支持任意时间点恢复,不用再自己写 crontab 脚本。
  • 高可用能力:高可用版实例默认会做主备切换,主库发生故障时自动恢复,应用侧只会在切换瞬间感知到连接抖动。
  • 参数模板和监控告警:RDS 提供了完整的监控项,连接数、CPU、内存、磁盘、慢查询、死锁都能看到,还能直接配置告警规则。

当然,托管不等于万能。RDS 实例在参数上会有一些限制,比如部分高级参数不能随便改,某些超级权限被收回,SQL 如果有特殊需求需要提前测试。这个在后面实操章节会详细说。

1.3 迁移之前必须想清楚的几个决策

迁移数据库不能上来就迁,有几个决策必须在动手前敲定:

第一,目标实例的版本。如果原库是 MySQL 5.7,我建议直接选用 RDS MySQL 8.0。一方面 5.7 已进入生命末期,另一方面 8.0 的默认字符集是 utf8mb4,对中文和 emoji 支持更友好。但要注意,如果应用里用了老版本特有的 SQL 写法,或者依赖 mysql_native_password 认证插件的老客户端,就要先做兼容性检查。

第二,目标实例的规格。不要直接照搬 ECS 的 CPU 内存配置。RDS 的规格选择要看实际负载,而不是物理机器配置。我这个小应用,ECS 是 4 核 8G,但 MySQL 在实际运行中 CPU 使用率连 20% 都不到,所以 RDS 我选了 2 核 4G,预留一部分突发能力。

第三,网络规划。RDS 在工作时是通过专有网络连接的,应用所在的 ECS 必须和 RDS 在同一个 VPC 下,或者通过云企业网打通。这一步如果前期没规划好,后面连不上数据库,最容易让人抓狂。

第四,停机窗口。小应用迁移最大优势就是可接受的停机时间可以压缩。我这次和业务方约的是周日凌晨 1 点到 3 点,预留 2 小时窗口,实际观察下来在线迁移方式几乎可以做到不停机。

2. 迁移前准备:盘点业务依赖和数据库家底

2.1 盘点应用侧对数据库的使用方式

在正式迁移前,我花了一天时间梳理应用和数据库的交互方式,具体做了三件事:

  1. 从应用代码里搜索所有直接执行 SQL 的地方,确认用了哪些数据库账号、连接的是哪个库、有没有跨库查询。
  2. 把 MySQL 通用日志短暂开启半天,抓取实际运行的 SQL 样本,确认有没有定时任务在跑存储过程、触发器或事件。
  3. 确认应用连接串的配置位置,是写在配置文件里,还是通过环境变量注入,方便迁移后快速切换。

这次排查发现了一个容易踩坑的点:应用里有一个定时任务,每天凌晨会通过一个只读账号执行报表查询,而这个账号是应用代码里硬编码创建的,并没有纳入统一账号管理。RDS 默认不允许通过 SQL 语句直接创建高权限账号,必须先通过控制台创建账号再授权。所以我在迁移前就规划好了目标实例的账号体系,模拟了原实例的账号权限模型。

2.2 数据量、字符集和排序规则的评估

数据量直接影响迁移方案的选择。这次实例总数据量 180GB,其中一个大表占了 110GB,单表行数超过 2 亿。这种体量如果用 mysqldump 全量导出再导入,整个流程会在导出导出两端都消耗大量时间,而且很容易在导入阶段碰到索引构建慢的问题。所以我直接把 DTS 在线迁移作为首选方案,逻辑备份方案只作为补充预案。

字符集方面,源库是 utf8mb4,目标库也保持 utf8mb4,避免中文和特殊字符出现乱码。这里有个细节:MySQL 8.0 的默认排序规则是 utf8mb4_0900_ai_ci,而 5.7 里常见的排序规则是 utf8mb4_general_ci。如果应用里的 ORDER BY 语句依赖特定排序规则,迁移后可能出现排序结果不一致。我在迁移前就把目标实例的库表字符集和排序规则都显式指定成和源库一致。

2.3 账号权限与安全组规划

RDS 的账号体系分成“高权限账号”和“普通账号”两类。高权限账号可以管理所有库,普通账号只能管理被授权的库。这个和自建 MySQL 里用 root 一把梭的习惯很不一样。我在目标实例上创建了一个与应用同名的业务账号,只授予了业务库的增删改查权限,并单独创建了一个只读账号给报表任务用。

网络访问控制上,RDS 默认启用了 IP 白名单。迁移前我把应用 ECS 的私网 IP 加进白名单,并严格限制只允许内网访问。这里特别提醒:不要把 0.0.0.0/0 直接加进白名单,尤其是在生产环境,这是云数据库安全的基础底线。

3. 数据迁移实操:三种方案选型与完整执行步骤

3.1 方案一:mysqldump 逻辑备份 + mysql 导入

这是最传统的方案,适合数据量小(比如 10GB 以内)、停机时间充裕的场景。步骤并不复杂,但每一步都有细节。

先导数据,注意参数:

mysqldump -h 源库地址 -u 迁移账号 -p \ --single-transaction \ --set-gtid-purged=OFF \ --routines --events --triggers \ --databases myapp > myapp_backup.sql

几个参数说明一下:--single-transaction在 InnoDB 表上通过开启一个可重复读事务来保证备份一致性,不锁表;--routines导出存储过程和函数;--events导出定时事件;--triggers导出触发器。我见过不少人在这一步漏掉 routines,结果迁移完发现存储过程全没了。

导入目标库:

mysql -h rds地址 -u 业务账号 -p myapp < myapp_backup.sql

导入前先确认目标库字符集和排序规则,导入过程中用tee记录输出,方便排查报错。这套方案我用在一次测试库迁移上验证过,1GB 的库大概 3 分钟能完成。但对 180GB 的生产库,我不建议用这个方案,原因很简单:导出 40 分钟,导入至少 1 小时,加上中途可能出现的索引重建,整体耗时太长,而且源库是单实例,导出过程对线上有一定性能影响。

3.2 方案二:DTS 数据传输服务做全量加增量迁移(推荐)

这是我这边的正式方案。DTS 的核心逻辑是先做一次全量数据迁移,然后持续同步源库产生的增量数据,等两边追平后,在业务低峰期做一次短暂的写操作切换,实现近乎不停机的迁移。

具体步骤分四步:

第一步,在控制台创建迁移任务。源库类型选 MySQL,接入方式选“有公网 IP 的自建数据库”或“ECS 自建数据库”,源库实例地区对应到 ECS 所在地域。目标库选“瑶池数据库 RDS MySQL”。任务类型勾选“结构迁移 + 全量数据迁移 + 增量数据迁移”。

第二步,配置源库和目标库的连接信息。需要源库的 IP、端口、数据库账号,我建议单独创建一个迁移专用账号,只授予 SELECT、LOCK TABLES、SHOW VIEW、REPLICATION CLIENT、REPLICATION SLAVE 这些权限,避免在源库上暴露过多权限。同时源库需要开启 binlog,且 binlog_format 必须设置为 ROW,否则增量同步会失败。

第三步,启动任务并观察状态。DTS 会先跑结构迁移,再跑全量数据迁移,最后自动进入增量同步阶段。全量阶段我重点看两个指标:迁移速度和延迟。一旦进入增量同步,源库和目标库的数据就会保持准实时一致。

第四步,业务切换。在确认增量同步延迟持续低于 5 秒后,我申请了一个维护窗口。窗口开始时,先停掉应用对源库的写操作(我是通过暂时把应用实例缩容并停掉定时任务实现的),然后确认延迟归零,最后修改应用连接串指向 RDS,重启应用服务。

这里要提醒一个关键点:连接串切换后,一定要检查“存量连接”是否都断干净了。因为 DNS 或者连接池缓存,可能有一些应用节点还保持着源库的旧连接,导致写操作又回到源库。我这次是在切换后,直接用命令查了源库的 processlist,确认没有来自应用的连接才放心。

3.3 方案三:基于物理备份或克隆实例的快速迁移

如果你有超大数据库(TB 级别),mysqldump 和 DTS 的全量初始化阶段都会比较慢。这时可以考虑物理备份迁移:先用 xtrabackup 在源库做物理全量备份,把备份文件传到目标环境后恢复。云上还可能用到 DTS 支持的“迁移可用区”或“从备份创建实例”等功能,但这个方案对大多数小应用都过于重了。

我这次没有采用物理备份,因为 180GB 在 DTS 全量阶段表现已经不错,而且物理备份需要对 xtrabackup 的兼容性做验证,额外的工作量并不划算。但如果你手里有几百 GB 甚至 TB 级的库,建议提前评估物理方案,别等到全量迁移跑到一半才后悔。

3.4 迁移后的校验清单

迁移完成不代表结束,数据一致性校验必须做。我习惯检查以下项目:

  • 表数量:通过 information_schema.tables 对比源库和目标库每个库的表数量是否一致。
  • 行数:抽样对比大表的 COUNT(*) 结果,尤其是超过千万行的表。
  • 自增主键:确认 AUTO_INCREMENT 的下一值与原库一致,避免插入数据时出现主键冲突。
  • 存储过程、触发器、事件:核查这些对象是否完整迁移。
  • 字符集:抽查中文数据、emoji 特殊字符,确认显示正常。
  • 权限账号:确认目标库的账号授权和原库一致,特别检查应用账号对关键表的权限。

我用一段 SQL 快速统计了表行数偏差:

SELECT table_name, table_rows FROM information_schema.tables WHERE table_schema = 'myapp' ORDER BY table_name;

然后和源库的输出做 diff,发现有一张表行数不一致。排查原因是有频繁写入,COUNT() 的采样信息存在延迟。于是我用真实 COUNT 再对比一次。这里也给同行提个醒:information_schema 里的 table_rows 是估算值,不适合做精确校验,只能做粗筛,最终以 COUNT() 为准。

4. 成本对比:小应用视角下的真实账本

4.1 自建 MySQL 的成本构成,别只看 ECS 价格

很多人对比自建和云数据库,只盯着 ECS 实例的价格,然后得出“自建更便宜”的结论。这是最大的误区。自建 MySQL 的成本至少包含五块:

  • ECS 实例费用:按 CPU 内存规格计费,包年包月或按量付费。
  • 云盘存储费用:数据盘、系统盘都需要单独付费,我这边用的是 ESSD,容量 300GB。
  • 备份存储费用:mysqldump 出来的备份文件还要传 OSS,OSS 的存储费用和流量费用都得算进去。
  • 运维人力成本:升级内核、修复漏洞、处理故障、优化慢 SQL,这些时间换算成成本,月均至少 0.5 个工作日。
  • 高可用成本:如果要搭主从,至少要再买一台 ECS 和一倍的云盘。

那天我把自建方案的费用逐项拉出来,发现 ECS 加云盘加 OSS 的死人成本并没有想象中低,再加上升级到 MySQL 8.0 需要的额外测试工作量,自建方案的综合支出已经逼近托管方案了。

4.2 瑶池 RDS 的成本构成,其实更透明

瑶池 RDS 的计费模型相对简单,主要三个维度:

  • 实例规格:按 CPU、内存规格计费,可以选择包年包月或按量付费。
  • 存储空间:包含数据容量和日志容量,超出赠送备份空间的部分另计。
  • 备份空间:实例赠送一定额度的备份空间,超过后按实际占用收费。

以我这个 2 核 4G、存储 100GB 的实例为例,包年包月的费用大约是自建方案中一台同规格 ECS 的 1.2 到 1.5 倍,但省掉了云盘费用、备份脚本维护、高可用搭建的额外成本。最关键的是,RDS 的高可用版自动提供主备切换,而自建方案中主从同步的搭建、监控和切换演练,至少需要连续几天的开发和验证。

4.3 三年 TCO 视角下的成本对比

我列了一张对比表,把三年内可能涉及的主要花费做了个粗略估算,仅供参考,具体价格以控制台实时报价为准:

成本项ECS 自建 MySQL瑶池 RDS MySQL
云资源(实例+存储+备份)ECS 4核8G + 云盘300GB + OSS备份,年费用约 6000-9000 元2核4G + 100GB存储 + 备份空间,年费用约 5000-7000 元
高可用部署额外一台ECS+主从配置,年增加约 3000-5000 元高可用版自带主备,差价已在规格中体现
运维工时每月至少半天处理备份、优化、故障几乎可以忽略,监控告警自动处理
升级成本版本升级需要大量测试,风险高控制台选择版本,升级过程平台承担

从三年 TCO 来看,自建方案的数据存储其实更灵活,因为你可以自由控制磁盘容量;但加上高可用和运维,托管方案的总体拥有成本反而低。对小应用而言,RDS 的开销并没有传说中的那么“贵”,关键在于规格别一步到位买太高,先按实际监控数据选型,后续不够再加。

4.4 成本中容易忽略的隐藏项

说几个容易踩的隐藏费用坑:

  • IOPS 费用:部分 RDS 存储类型会在超出基础 IOPS 后按量计费。如果业务有大量全表扫描或频繁刷脏页,IOPS 可能成为隐性开销。
  • 只读实例费用:如果需要扩展读能力,额外的只读实例是单独计费的。
  • 公网流量费用:在使用公网连接 RDS 时,会产生流量费用。生产环境建议保持内网访问,既安全又省钱。
  • 数据迁移工具费用:DTS 在特定功能或数据量下可能产生额外费用,迁移前先看帮助文档确认计费模式。

5. 迁移过程中的典型问题与排查实录

5.1 字符集导致的中文乱码

迁移完成后第一次用应用写数据,发现新写入的中文在页面上显示正常,但个别旧数据里出现了“?”。排查后发现,问题不在迁移,而是应用连接串里没有指定 characterEncoding,导致写入按平台默认编码处理。这个在自建环境里因为服务器 locale 一致没暴露,换到 RDS 后连接参数就变得敏感了。

解决办法是应用侧 JDBC 连接串显式加上characterEncoding=utf8mb4,并在 RDS 控制台把实例和库表的字符集统一设置为 utf8mb4。这里注意:连接串的字符集和数据库字符集最好都设置,双保险。

5.2 自增主键冲突

迁移过程中,由于 DTS 在全量迁移阶段没有同步自增序列的当前值,导致目标库的 AUTO_INCREMENT 值比源库小,切换后应用插入数据时碰到“Duplicate entry for PRIMARY KEY”报错。

当时第一时间去目标库执行了:

ALTER TABLE my_big_table AUTO_INCREMENT = 2200000001;

把自增值调整为略大于源库当前最大 ID 的值。这个操作在 MySQL 8.0 上执行很快,但如果是大表,InnoDB 会在内存中重建自增计数器,建议在低峰期操作。

5.3 连接池参数导致连接数打满

RDS 对最大连接数有默认限制,比如 2 核 4G 的实例默认最大连接数是 800。如果应用连接池的 maxActive 配置得太大(比如 200),并且在多个节点上部署,连接数就很容易打满。

我这次遇到的是应用连接池初始化和预热导致短时间连接数突增。排查思路是先看监控里的“当前连接数”,再通过 processlist 查看连接来源,确认是哪个应用节点在创建连接,然后适当调小连接池上限。小应用的并发通常不高,连接池设成 50 到 100 完全够用,没必要追求大水体配置。

5.4 慢 SQL 在云数据库上更明显

迁移后我发现一些查询的耗时略有增加。这主要是实例规格比原来的 ECS 低一档导致的,并非 RDS 性能差。排查方式是在 RDS 控制台开启慢查询日志,发现有一条带全表扫描的排序查询,在源库时因为数据量小不明显,迁到新实例后索引失效。

解决方法是给查询涉及的字段补一个组合索引:

ALTER TABLE order_list ADD INDEX idx_user_time (user_id, create_time);

这条 SQL 执行前,我先在测试环境验证了执行计划,确认走索引后才在生产执行。RDS 的慢查询日志和 SQL 洞察功能在这种场景下特别有用,能直观看到每类 SQL 的耗时分布,这也是自建环境很难具备的体验。

5.5 只读账号和权限边界的问题

最后说一个和权限相关的坑。原来自建环境里,应用用一个账号,DBA 用另一个账号,但 DBA 账号权限几乎是全部授权。到了 RDS 后,控制台创建的账号默认不允许 GRANT OPTION,也就是说普通账号不能把权限转授给其他账号。如果有业务需求需要动态创建账号,得通过高权限账号操作,或者提前在控制台规划好。

此外,RDS 部分系统库(如 mysql 库)的访问权限默认不开放,如果应用里存在恶意的“跨库查询”,比如直接查询 mysql.user 表,迁移后会直接报权限不足。这样的代码必须提前改掉。

写在最后:这次迁移我学到的东西

整个迁移从评估到切换,前后花了一个多星期,真正在维护窗口里操作的时间不到半小时。最大的感受是,小应用做数据库上云,难点从来不在“导出导入”这个动作,而在前期的依赖梳理、字符集与权限规划,以及切换后的校验。自建 MySQL 并不是不行,但前提是你有足够的精力持续维护它;对一个只有两三个后端同学的小团队来说,把数据库运维交给瑶池数据库 RDS 这样的托管服务,确实能省下不少时间去做业务。

再补一个小技巧:迁移切换前一晚,我把源库的数据目录和备份文件都做了一次快照或异地备份,以防万一目标库出问题还能快速回滚。结果迁移非常顺利,这份备份虽然没派上用场,但心里踏实了很多。对于数据库迁移这类“只能成功”的操作,多点保险措施永远值得。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询