先把一个真实场景摆在这儿:单节点 k8s 上跑着一整套若依微服务,新环境是阿里云 ECS,要求“准不停服、不丢数据”地迁过去,迁移完还要让压测人员拿着 jmeter 脚本跑高并发,验证云上能不能扛得住。
这个需求单看每一句都不难,但合在一起就相当棘手。单节点 k8s 意味着底层没有现成的分布式存储和高可用能力,本地盘数据跟着 Pod 走;若依微服务套件里光是 Nacos、网关、认证中心、系统服务、定时任务、监控这一串组件,启动顺序和配置依赖就能让人头大;更别说“准不停服、不丢数据”这八个字,几乎把所有简单粗暴的搬迁方案都堵死了——你不能停服慢慢拷数据,也不能在新环境启动完才发现业务表少了几张。
这篇文章我就按实际操作的顺序,把这次迁移从盘点、同步、切换、压测到回滚的完整链路拆开讲。包括为什么在迁移场景里优先选 MySQL 主从复制而不是停服导入、蓝绿部署在这次迁移中的具体落地姿势、灰度流量怎么放、以及最后压测环节怎么读 jmeter 的结果才不算白跑。全程以“一个干过这事的人”的视角来讲,不会只丢概念。
1. 先搞清楚要搬什么:组件盘点是迁移的第一道坎
接到“迁移整套若依微服务”这个需求时,最容易犯的错误是直接去倒数据库。实际上在动手之前,有几件事必须做,否则后半程一定返工。
1.1 若依微服务在单节点 k8s 上的典型组件画像
一个标准的若依微服务(RuoYi-Cloud)环境,代码层面通常是后端若干个 Spring Cloud Alibaba 服务模块加一个前端 Vue 项目。落到运行时,容器里大致是这样一组角色:
| 组件 | 作用 | 迁移时是否需要处理数据 |
|---|---|---|
| Nacos | 服务注册与配置中心 | 需要,配置和持久化数据要带过去 |
| Gateway 网关 | 统一入口,路由转发 | 不需要,但要注意路由配置 |
| 认证中心(Auth) | 登录鉴权、令牌管理 | 需要,涉及用户会话数据与密钥配置 |
| 系统管理服务(System) | 用户、角色、菜单等核心业务 | 需要,数据库为主 |
| 定时任务服务(Job) | Quartz 任务调度 | 需要,任务状态和触发记录在库里 |
| 文件服务(File) | 上传下载,通常存本地磁盘 | 需要,文件目录是隐藏雷区 |
| MySQL | 业务数据库,核心中的核心 | 核心,需要完整迁移 |
| Redis | 缓存、token、验证码 | 需要,可容忍部分丢失,但最好同步 |
| Nginx/前端静态资源 | 页面入口 | 不需要数据,但切换入口要靠它 |
| 监控组件 | 可选,如 Prometheus 等 | 不迁移也能跑,按需处理 |
这里最容易被低估的是“文件服务”。若依的上传功能默认可能写到容器内或挂载的本地路径,单节点 k8s 上用 hostPath 挂载很常见。如果不提前梳理文件目录,迁移完数据库后你会发现历史附件全部 404。
1.2 单节点 k8s 环境带来的特殊约束
单节点 k8s 和正规多节点集群有个本质区别:没有跨节点调度和持久化存储兜底,很多组件跑在“客厅里放了个书架子”这种弱约束状态。具体到迁移场景,影响主要有三点:
第一,镜像可能只在本地存在。单节点上如果用过 docker build 或者在节点本地加载过镜像,新环境里不一定有现成的镜像仓库可拉。迁移前先把镜像导出或推到私有仓库,永远是最稳的做法。
第二,很多服务的配置是通过 Nacos 统一管理的,但 Nacos 本身又跑在同一套 k8s 环境里。这就形成了“配置依赖服务,服务又依赖配置”的循环。迁移时如果只搬业务库不搬 Nacos 数据,新环境里服务会因为读不到配置而启动失败,而且报错往往很隐蔽。
第三,本地盘数据没有副本。迁移过程中如果源节点磁盘出问题,连个备份节点都没有。所以在全量数据同步之前,必须先做一次完整的离线备份,当作保底,而不是直接开始同步。
1.3 迁移前必须产出的三份清单
这里我建议动手之前先花半天时间,把下面三份清单整理出来。看起来是体力活,但后面每一步都在给它们还债。
端口与依赖清单。若依服务的默认端口要全部列出来。网关 8080、Nacos 8848、MySQL 3306、Redis 6379、前端 80/443,再加上各个微服务的内部端口。这些端口决定了迁移后的安全组规则和 Nginx 转发配置,少放一个端口,压测时就多一个超时瓶颈。
数据目录清单。逐个查看 Deployment / StatefulSet 的挂载卷,确认哪些是 emptyDir(容器重建即丢)、哪些是 hostPath(落在节点本地)、哪些用了 PV/PVC。文件服务、Nacos 的持久化目录、MySQL 的 datadir,都在这份清单里重点标注。
启动依赖顺序清单。若依这套微服务有明确的启动依赖:先 Nacos,再 MySQL/Redis,然后认证中心、系统服务,最后才是网关和前端。迁移时如果顺序乱了,服务虽然能起来但注册不到 Nacos 上,调用链直接断掉。这个清单也决定了新环境的部署编排。
2. 数据同步方案选型:为什么我选了主从复制而不是停服拷贝
“准不停服、不丢数据”这个需求,直接否决了最简单的方案:停应用、导 SQL、导完再启应用。所以核心思路必须是有重叠期的同步方案——旧环境继续提供服务,新环境通过实时同步追数据,两边数据追平后再切换流量。
2.1 全量 + 增量两段式迁移的整体思路
数据同步的标准做法是“全量初始化 + 增量追平”。第一步,把源库某个时间点的全量数据导入新库;第二步,从那个时间点开始,源库产生的新变更实时同步到新库;第三步,等增量延迟归零或接近归零时,选择业务低峰期切换。
时间线的具体展开是这样的:
- T0 时刻:源库做全量备份,记录当时的 binlog 文件名和位点(或 GTID)。
- T0 到 T1:全量备份传输到新环境,导入新库。这个过程源库不停服,业务照常写入。
- T1 时刻:新库导入完成,开启主从复制,从 T0 的位点开始拉取增量 binlog。此时新库开始追上 T0 之后产生的所有变更。
- T1 到 T2:观察延迟,等 Seconds_Behind_Master 趋近于 0,新库和源库的数据基本一致。
- T2 时刻:切换流量到新环境,源库停写或降级为只读,数据完成迁移。
这套流程的优点是几乎不依赖业务停机时间,唯一的“不可用窗口”只有切换那一瞬间——而且这个窗口还可以通过灰度方式进一步压缩到几乎无感。
2.2 全量备份工具选择:mysqldump 还是 XtraBackup
全量备份这一步,工具选型直接决定了你的停服时间成本和后续增量同步的起点获取方式。
mysqldump是老牌工具,胜在简单和兼容性好。对若依这种数据量级(通常几十 GB 以内)完全够用。它导出的 SQL 文件可读性好,换环境执行时出错容易排查。但缺点是逻辑备份速度偏慢,而且不带 ibd 物理文件,大批量数据时导入时间长。
XtraBackup是物理备份工具,备份和恢复速度都快,能直接按 datadir 物理文件还原,而且备份过程中几乎不阻塞写入。但它对低版本的 MySQL 兼容性要仔细确认,另外恢复时对新机器的新版本 MySQL 适配度不如 mysqldump 干净。
我的建议是:数据量小于 20GB,直接上 mysqldump 单事务模式,简单粗暴;数据量大或者对停机时间极度敏感,选 XtraBackup。
举个例子,用 mysqldump 做一致性快照并拿位点,命令大致是这样:
mysqldump \ --single-transaction \ --set-gtid-purged=ON \ --master-data=2 \ -h 源库地址 -u迁移账号 -p \ ruoyi_cloud > ruoyi_full.sql注意--single-transaction利用 InnoDB 的 MVCC 机制,在不锁表的情况下拿到一份一致性快照;--master-data=2会在备份文件头部注释里自动写上 SHOW MASTER STATUS 的信息,也就是 binlog 文件名和位点,这个信息是后面起增量同步的关键。
拿到这个文件后,在目标库执行:
mysql -h 新库地址 -u迁移账号 -p < ruoyi_full.sql导入完成后不要急着开主从复制,先全库校验一遍基础表数量和关键表行数,确认没有导入丢数据再继续。
2.3 增量同步的核心:开启 binlog 并确认位点
增量同步的本质就是把源库的 binlog 回放到新库。所以源库必须满足一个前提——binlog 功能本来就是打开状态,而且格式设置为 ROW。若依环境通常在搭建时就开启了,但单节点 k8s 里 MySQL 的配置五花八门,这一步必须核实:
[mysqld] log-bin=mysql-bin binlog_format=ROW server-id=1 gtid_mode=ON enforce_gtid_consistency=ON在 MySQL 8.0 里推荐直接开启 GTID 模式,后面做主从切换会省非常多事。GTID 是全局事务标识符,每一个提交的事务都有一个唯一 ID,通过 GTID 找同步位点比单纯的“文件名 + 位点”要可靠得多,不会出现文件轮转后位点错乱的问题。
确认方式也简单:导入完全量备份后,在源库执行SHOW MASTER STATUS;,记录 File 和 Position(或 GTID 集合),然后在目标库执行:
CHANGE MASTER TO MASTER_HOST='源库地址', MASTER_USER='repl_user', MASTER_PASSWORD='密码', MASTER_PORT=3306, MASTER_AUTO_POSITION=1; START SLAVE;注意若依的业务是持续写入的,如果 binlog 文件已经轮转过,用 GTID 模式能自动跳过已经执行过的事务,这是 ROW + GTID 组合最大的优势。
2.4 主从复制跑起来之后,延迟是唯一的敌人
主从复制开启后,大概率会遇到一个现象:延迟数字忽高忽低。对新环境来说这是正常的,因为全量导入后积压了一批增量,追平需要时间。但持续观察半小时后如果延迟还是很大,就要排查了。
最大的嫌疑是网络带宽。源库到新库如果走公网,跨地域延迟会放大同步延迟,而且大事务(比如定时任务批量更新几千行)在 ROW 格式下会产生大量 binlog 事件。这种场景常用的缓解手段是先走内网或专线同步,或者先用低峰期追平,压测阶段再拉专线。
另一个常见坑是主库上没有建对复制账号。错误提示往往是Authentication plugin 'caching_sha2_password' cannot be loaded。如果源库是 MySQL 8.0,目标库或中间工具用的是旧驱动,就得把账号插件改成 mysql_native_password,或者用高版本客户端重连。
延迟追平后,还需要做一次数据校验。MySQL 生态里常用 pt-table-checksum 工具扫描主从数据一致性,如果不想引入额外工具,也可以在关键业务表上跑行数对比和 max(id) 对比。若依场景中,核心校验表至少包括 sys_user、sys_role、sys_menu、gen_table 等几张基础表,业务表按实际情况增加。
3. Redis 与文件数据的同步策略:容易翻车的隐藏副本
主从复制解决的是 MySQL 的增量同步,但若依这套微服务里还有两个“数据”是真的容易在迁移时被忽略的:Redis 里的缓存数据和文件服务里的附件文件。
3.1 Redis 数据要不要迁、怎么迁
先回答一个很多人纠结的问题:Redis 缓存能不能丢?答案是“可以部分丢,但不能全丢”。若依的验证码存储、登录 token、接口限流计数都存在 Redis 里。如果全丢,最直接的后果是用户全部掉登录,体验非常差;但如果只是部分 Session 失效,用户重新登录一次就恢复了。
所以对 Redis 迁移,我的做法是:优先做全量持久化迁移,但允许小概率丢失。
具体操作是在源 Redis 先执行BGSAVE生成 RDB 快照,然后把 dump.rdb 文件传送到新环境 Redis 的持久化目录,重启新环境 Redis 加载即可。这套流程在数据量不大的场景下,迁移时间在分钟级,业务影响几乎为零。
如果你用的是 AOF 持久化,原理类似:先BGREWRITEAOF,拿到 AOF 文件后迁移到新环境,但要注意两个环境的 AOF 配置要一致,否则加载时会有兼容性问题。
Redis 迁移完不是终点,还要检查应用配置里的 Redis 地址是否已经指向新环境。这个配置在若依里通常通过 Nacos 管理,改错了会导致“MySQL 已经切到新库,Redis 还在旧库”的尴尬局面。
3.2 文件服务的迁移思路
文件服务是单节点 k8s 迁移里最容易被忽略、也最容易出错的部分。因为从数据库主从复制的视角来看,它根本不在复制范围内,但它对业务的影响是“数据”级的。
如果你的若依文件服务配置的是本地磁盘或 hostPath,那么迁移思路就是:rsync 全量同步 + 增量同步,和数据库的“全量 + 增量”思路完全一致。
先做一次全量同步:
rsync -avz --progress \ /data/ruoyi/upload/ \ 新环境IP:/data/ruoyi/upload/然后在切换时间点前再做一次增量同步,保证切换时只差最后几分钟的文件变动。如果文件量大、持续写入,可以加密定时的增量同步任务,直到流量切换前五分钟再停止。
这里有个经验:不要试图把文件服务的同步和数据库同步放在同一个时间窗口完成,因为文件写入的“一致性点”很难定义。更稳妥的做法是先同步文件,再同步数据库,最后统一切换。文件同步多跑几轮没坏处,数据库追平了才是关键点。
4. 应用层迁移:镜像搬运、配置切换与启动顺序
数据层准备好之后,开始动应用层。这部分有两个大的方向:一是把旧环境的镜像和配置整个“搬”过去,二是在新环境里重新部署一套。在单节点 k8s 到云上 ECS 的场景中,我更推荐第一种——“镜像搬运 + 配置同步”。
4.1 单节点 k8s 的镜像导出与仓库中转
单节点 k8s 里的镜像通常分散在节点的 containerd 或 docker 缓存里,没有统一的仓库管理。迁移前需要把所有服务的镜像名和 tag 列出来,然后在源节点上逐个导出。
如果节点用的是 docker,可以用:
docker save 镜像名:tag -o 输出文件.tar如果是 containerd 运行时,则用ctr或nerdctl导出。导出的 tar 文件传到新环境再 load 回去。这个方式操作直接,但有个致命问题——镜像 tar 文件可能非常大,若依整套微服务下来,十几二十个镜像合计十几个 GB 很常见。所以还是建议在迁移过程中顺手搭一个本地镜像仓库(比如 Harbor 或简版 registry),镜像推到仓库里,新环境直接拉取。
这一步还有个隐藏收益:镜像仓库会成为后续发布的基础设施。这次迁移完,后面做蓝绿部署或者灰度发布时,镜像仓库就是发布流水线的起点。
4.2 Nacos 配置迁移:最容易踩坑的环节
若依微服务的配置高度集中在 Nacos。迁移时如果只搬 MySQL,到了新环境一定会出现服务启动后疯狂报错的现象,比如数据源连的还是旧地址、Redis 地址未改、各个微服务之间注册不到正确的服务名。
正确做法是:在旧环境 Nacos 里导出全部配置,在新环境 Nacos 里重新导入,然后把所有涉及“环境相关的地址”统一改成新环境的 IP 或域名。
具体要改的配置项至少包括:
- Spring Cloud Alibaba 中 Nacos server 地址
- MySQL 数据源地址
- Redis 连接地址
- 文件服务保存路径或上传域名
- 各微服务之间的调用地址(如果用了直连而非服务发现)
- 日志路径
这里提醒一句:若依环境下,Nacos 的配置中心数据和注册中心数据往往会混着谈。迁移时配置中心数据要完整导入,但注册中心里的服务实例数据不需要手动导——服务启动后会自动注册上去。如果你在新环境 Nacos 里看到一堆旧实例的残留,直接在配置里把preserved.heart.beat.timeout和临时实例过期时间调短,等旧实例自动下线即可。
4.3 从 Deployment 到 YAML 的适配
单节点 k8s 上的 Deployment、Service、Ingress 定义,通常可以直接搬到新环境,但有几处必须调整:
镜像地址。如果新环境使用私有仓库,所有镜像的 image 字段要改成新地址。
存储卷。单节点上如果用了 hostPath,到新环境后要么继续用 hostPath 指向相应目录,要么改造为云盘挂载。建议直接用阿里云的云盘 PVC,因为后续扩容和迁移都不用再折腾。
资源限制。若依微服务的 JVM 参数和容器 requests/limits 在单节点上往往没有严格配置,但这套东西压测时是要被反复考验的。迁移后在 Deployment 里明确写出内存 request 和 limit,避免压测时内存争抢导致 OOMKilled。
启动顺序。k8s 本身不保证 Deployment 之间的启动顺序,所以若依这种依赖 Nacos 先行的架构,需要靠 InitContainer 或探针来自动等待。对我来说最省事的做法是:把 Nacos、MySQL、Redis 这些基础组件的 Deployment 单独一组先启动,确认健康后再启动业务服务。
5. 切换发布:蓝绿、灰度、双活这一次全用上了
标题里提到的灰度发布、蓝绿部署、双活架构,在迁移切换这个环节正好全部有实际应用。切换不是“啪”一下改一个域名就完事,而是要拆成蓝绿切换、灰度放量、准双活观察三个步骤来走。
5.1 蓝绿部署在迁移中的具体含义
经典的蓝绿部署是准备两套完全一样的环境,一套“蓝”跑旧版本,一套“绿”跑新版本,通过负载均衡统一切换流量。
在这个迁移场景里,蓝绿部署的映射就是:旧环境 = 蓝,新环境(阿里云 ECS)= 绿。两套环境跑着同一套应用,数据库通过主从同步保持着准实时一致。切换工具就在最前面的 Nginx 或者 SLB 上,把流量从蓝切到绿。
这个方案的好处是“整环境一致”。切换前随时可以切回蓝环境,业务没有感知。对服务器数量有限、不想做细粒度拆分的团队来说,是最稳的方案。
具体落地时,前端 Nginx 的 upstream 里同时配置旧环境和环境,权重先设为旧环境 100%、新环境 0%。切换时把新环境权重逐步调大,直到新环境 100%。
5.2 灰度发布:用权重放量降低风险
蓝绿切换是全量瞬间替换,灰度发布则是让“新环境”先接一部分流量,验证没问题后再全量。
若依场景下灰度可以分三层来做:
第一层是内部验证流量。切换前新环境已经跑起来,服务注册正常。这时可以先让测试人员配 hosts 或单独域名访问新环境,跑几个核心链路:登录、列表查询、流程审批、文件上传下载。这层不承载真实用户,纯粹验证功能完整性。
第二层是低比例真实流量。在 Nginx 里把新环境权重设为 10%,旧环境 90%。这个阶段重点观察新环境日志有没有异常、数据库主从延迟有没有被拉大、Redis 命中率是否正常。观察周期建议至少一个业务高峰,比如观察半天或一天。
第三层是全量切换。新环境验证没问题后,权重设为 100%。此时旧环境进入“待回滚状态”,但先不回收资源,准备随时回切。
灰度发布和蓝绿部署的组合使用,是我在做这类迁移时最喜欢的方式:蓝绿保证“环境级”的兜底,灰度保证“流量级”的平稳过渡,两者叠加后能把切换风险压到最低。
5.3 双活架构在迁移场景中的边界认知
标题里的“双活架构”放在迁移场景中,很容易被理解成“两套环境同时对外提供服务、数据双向同步”。我必须泼一盆冷水:真正的双活需要应用层做拆分、数据库双向同步、冲突解决机制,复杂度远超一次迁移所需。迁移场景下我们做的其实是“准双活”或“主备双活”——两套环境都热着,但只有一端能写。
实现主备双活的关键在数据库:源库保持可写,新库通过主从复制实时同步。应用层两个环境都承接读流量没问题,但写流量只能打给主库。灰度放量那 10% 的流量要确保它的写操作也走源库,否则新库上产生了独立写入,主从复制链路就会出现主键冲突或数据不一致。
那么问题来了:如果新环境接受写操作呢?最简单的落地办法是,在切换前,新环境的业务服务里把数据库连接配置成源库的地址。这样虽然应用跑在新环境,但数据仍然写到旧库,然后通过主从复制回到新库。数据流上,新库只是“读 + 复制追写”,不会产生独立写入。等到切换时,再把数据库连接统一改成新库地址,并把源库设为只读。这套做法可以让你在不搭复杂双写中间件的情况下,安全度过灰度期。
5.4 流量切换的执行清单
把切换拆成可回退的小步骤是关键。我的执行清单大致如下:
- 切换前 15 分钟:确认 MySQL 主从延迟为 0,Redis 和文件服务最后一次增量同步完成。
- 切换前 5 分钟:在 Nginx 上把新环境权重调到 10%,观察日志异常。
- 观察 10~15 分钟:确认无接口报错、无鉴权失败、无文件访问 404。
- 权重逐步上调到 50%、100%。
- 全量切换后:把源库改为只读,观察新环境连接是否稳定、复制延迟是否回到 0。
- 观察一个完整业务周期后:停掉旧环境的写入口,保留只读备用。
整个过程要控制在半个小时内完成,所以尽量选业务低峰期操作,比如凌晨或周末。
6. 压测验证:jmeter 高并发测试怎么跑才能真正验证承载能力
迁移完成不等于工作结束。压测人员用配套的 jmeter 脚本做高并发测试,目的不是“跑一遍看会不会挂”,而是要看清楚新环境在不同并发水平下的表现,验证云上环境是否满足业务承载要求。
6.1 压测前需要确认的基线数据
在压测前,先和业务方对清楚几件事:目标最大在线用户数、核心接口的期望 RT(响应时间)、系统允许的最大错误率、平均每用户每秒请求数。没有这些基线,压测报告只是数字堆砌。
举例来说,如果业务方说“高峰期 5000 人在线”,按平均每个在线用户每分钟触发 10 个请求算,系统需要的吞吐量大约就是 5000 × 10 / 60,约 833 QPS。这个数值就是压测的基准吞吐目标。jmeter 脚本里的线程数和循环次数,就应该围绕这个目标来设计,而不是随便写个 1000 线程去跑。
6.2 jmeter 脚本的正确打开方式
拿到压测人员的 jmeter 脚本后,先别急着点 Start。我一般会先做三件事:
第一,确认脚本里的请求头是否有 token 依赖。若依这种带登录鉴权的系统,jmeter 脚本里通常会有一个获取 token 的 setUp 线程组,后续接口通过 HTTP Header 带上 token 调用。如果 token 在 Redis 里过期,压测过程中会出现大量 401 报错,压的其实是登录接口而不是业务接口。所以压测前要把 token 有效期调长,或者让脚本在 token 过期前自动重新登录获取。
第二,确认线程数对应的实际并发。jmeter 的线程数并不完全等于服务端的并发连接数,因为每个线程可能循环多次,而且思考时间(Think Time)的存在会降低实际压力。要看服务端真实并发,需要结合 nginx 的活动连接数和数据库的连接数来判断。
第三,压测脚本的断言不能只看 HTTP 200。如果业务接口返回状态码是 200,但 JSON 里的 code 字段是 500,这个请求实际是失败的。jmeter 里需要加上 JSON 断言,特别检查 code 字段是否符合预期。这一步直接决定压测结果的可信度。
6.3 压测中重点盯的四个指标
压测过程中,很多人只盯吞吐量和错误率,但我觉得至少要看四个维度的数据:
| 指标 | 说明 | 异常判断参考 |
|---|---|---|
| QPS/TPS | 实际吞吐量是否达到目标 | 明显低于目标且响应时间持续上涨,说明有瓶颈 |
| 响应时间 RT | 平均响应时间、P95、P99 | P99 突发上涨通常是慢查询或线程池排队 |
| 错误率 | 失败请求占比 | 超过 1% 就要中断压测排查 |
| 服务端资源 | CPU、内存、磁盘 IO、网络带宽 | 任何一项持续打满都可能是瓶颈点 |
除了 jmeter 的报告,还要同步看云监控上的数据。压测时如果 ECS 的 CPU 已经 100%,但 QPS 上不去,说明应用在单点资源上卡住了;如果数据库连接数被打满,大概率是连接池配置偏小或慢 SQL 过多。
6.4 高并发压测的常见瓶颈与调优方向
压测跑完发现问题很正常,关键是能不能快速定位。我用过的排查顺序是:先看 Nginx 入口有没有报错,再看网关日志有没有超时,然后看业务服务线程池状态,最后才看数据库慢查询。这个顺序符合一次请求的调用链路,能很快把问题圈定在哪一层。
若依这套环境压测时,有几个高频瓶颈点:
数据库连接池。Spring Boot 默认的 HikariCP 连接池,maximum-pool-size 设置过小的话,高并发下数据库连接会被抢光,报连接超时。压测前把这个参数调成和数据库 max_connections 匹配的值。
网关线程池。若依网关用的是 Spring Cloud Gateway,底层基于 Netty。压测时如果遇到大量 Connection reset 或超时,优先检查网关的线程数和后端服务的响应速度。
Redis 过期风暴。若依的验证码和会话数据大量存在 Redis,压测时如果短时间内创建大量 token,Redis 内存会上涨,如果没设置合理的内存淘汰策略,可能直接 OOM。
JVM 堆内存。单节点 k8s 上跑着的服务,容器内存限制经常和 JVM 堆参数不匹配。迁移到 ECS 后,记得同步调整 Xms/Xmx,避免容器内存限制 2G 而 JVM 堆只分了 512M 这种话都说不清楚的局面。
压测结束后,还建议留一组低频稳定性压测,比如维持 50% 目标并发跑 30 分钟,看看有没有内存泄漏、线程池堆积之类只能靠时间暴露的问题。这类问题在短时间高并发压测里往往看不出来,但对生产环境的长期稳定是致命的。
7. 回滚预案与实践避坑清单
最后聊回滚。迁移和发布最怕的不是出问题,而是出了问题不知道怎么回去。所以从切换到压测的每一步,都必须保留“回到旧环境”的能力。
7.1 回滚的三种级别与触发条件
回滚预案我习惯分三级:
级别一:切换过程中 Nginx 权重还没到 100%,发现错误率上升。直接权重归零,回到旧环境 100%,数据无需处理,因为源库一直是主库,没有产生分叉。
级别二:全量切换后一两个小时内发现问题。此时源库已被置为只读,但新库的数据是从源库同步过来的。回滚的方式是把源库从只读改回可写,Nginx 权重切回旧环境。要注意的是,这期间新环境如果有写入,会因为主库切回源库而丢失或冲突。所以从切换时刻起,暂停新环境的写入口,直到确认稳定后再放开。
级别三:切换完成超过一个业务周期,新环境产生了大量新数据。这时回滚复杂度会急剧上升,因为旧库已经落后于新库。如果业务允许,优先选择“保留新环境为主,修复问题”而不是回滚;如果必须回滚,则需要把新库的新增数据倒灌回旧库,本质上是一次反向数据迁移,耗时和风险都很大。
所以我的经验是:前两种级别要预案到位,第三种尽量在架构上避免发生。迁移前把“全量切换后新环境至少观察 24 小时”写进流程里,就是为了避免业务在新环境上产生大量数据后不得不面对复杂回滚。
7.2 迁移与发布实战中的高频坑清单
这部分是我觉得全文最有价值的地方。一次迁移踩过的坑,比读十篇文档收获都大。我按出现的频率排一下:
| 坑 | 现象 | 处理方案 |
|---|---|---|
| MySQL 主从复制报 1236 错误 | 从库拉不到 binlog,日志报 Could not find first log file name in binary log index | 全量备份时的位点已经失效,需要重新做一次全量初始化。通常是备份和数据变更时间隔太久导致 binlog 轮转清理了 |
| 文件上传后前端访问 404 | 数据库迁移成功但附件目录没同步 | 压测前把上传接口和附件访问接口都跑一遍,确认文件系统路径 |
| Redis 数据迁移后应用读不到 | 部分 token 在旧 Redis 里,新 Redis 没有 | Redis 迁移完成后,把服务滚动重启一次,强制缓存重建 |
| Nacos 配置导入了但服务报数据源错误 | 配置里的 MySQL 地址还是旧的 | 导完 Nacos 配置后全局搜索旧环境 IP,全部替换 |
| 压测时大量 Timeout 但 CPU 不高 | 网关线程池排满,或数据库连接池不够 | 看线程池活跃数,调大 connection-pool-size 或网关 worker 线程数 |
| 灰度权重 10% 时出现了重复数据 | 新环境某服务把数据直接写到了源库,同时主从复制又把源库数据同步回新库 | 切换前把新环境业务库地址统一改回源库,避免双写风险 |
7.3 最后一件事:压测通过后别急着回收旧环境
压测验证通过、新环境承载能力符合预期后,我建议旧环境至少在保留 3 到 7 天再回收。原因有两个:一是业务人员在实际使用中可能会发现压测没覆盖到的问题,保留旧环境随时可以回切;二是万一新环境出现数据问题,旧环境的历史数据还能作为备份恢复源。
这个阶段如果觉得旧环境占资源,可以把旧环境的服务缩容到最低副本数,但数据库和文件服务保留原样。等新环境稳定运行一周,再把旧环境的备份数据做一次最终归档,整个迁移才算真正画上句号。
迁移这件事,本质上不是在搬服务器,而是在转移“信任”——把业务对旧环境的信任,平稳地转移到新环境上。数据同步、备份、切换策略、压测验证,这些手段都是为了让这个转移过程可控、可信、可回退。希望这篇实战记录能帮你少走几步弯路。