数据库迁移至美国NVMe服务器前,如何检查容量、备份与写入监控?

把数据库迁移到美国NVMe服务器前,先不要急着复制数据。美国NVMe服务器提升数据库性能的前提,是目标端容量足够、备份可以恢复、写入量能够被持续观察,并且切换失败时仍能回到源端。NVMe只改善存储路径,不能弥补磁盘空间不足、备份不可用、事务日志堆积或查询与锁竞争。
最小可用方案可以先满足六项条件:确认数据库版本和数据目录、盘点真实容量、完成一次可验证的备份、在目标端完成隔离恢复、建立写入监控、制定不丢数据的回滚边界。读写分离、持续复制、独立备份盘和可视化监控可以后续增加,不必在第一次迁移中全部引入。
先明确迁移前的最低条件
| 检查项目 | 必须确认的内容 | 不满足时的处理 |
|---|---|---|
| 数据库版本 | 源端和目标端的数据库主版本、扩展、字符集和时区 | 先在隔离环境完成兼容性测试,不要直接跨大版本恢复 |
| 数据容量 | 数据、索引、临时文件、事务日志或二进制日志的实际占用 | 重新计算目标端所需空间,不能只按业务表大小估算 |
| 备份 | 备份文件完整,能够在隔离数据库中恢复 | 备份未验证前,不进行正式切换 |
| 写入量 | 业务高峰和低峰的写入变化、日志增长和磁盘写入 | 先建立基线,再决定维护窗口或是否需要复制方案 |
| 目标环境 | 数据目录挂载点、文件系统、数据库用户、应用连接参数 | 目标端先完成基础部署和权限检查 |
| 回滚条件 | 切换前谁是权威数据源,切换后哪些写入可以回退 | 不明确时不要让目标端直接承接生产写入 |
如果无法取得至少一个业务高峰和低峰的数据,容量与写入判断只能作为估算,不能据此承诺具体承载量。迁移前应优先记录源端的数据库大小、磁盘使用率、写入日志增长、数据库连接数和备份文件大小。
一、先检查容量,而不是只看数据库表大小
1. 找到真实的数据目录和挂载点
先确认数据库版本与数据目录。下面以Linux服务器为例,路径需要替换为实际目录:
psql --version
sudo -u postgres psql -Atc "SELECT version();"
sudo -u postgres psql -Atc "SHOW data_directory;"
如果使用MySQL或兼容实现,应先确认版本和数据目录:
mysql --version
mysql -NBe "SELECT VERSION();"
mysql -NBe "SELECT @@datadir;"
确认路径后,再检查文件系统。不要把示例路径直接当成生产路径:
DATA_DIR=/var/lib/postgresql
df -hT "$DATA_DIR"
df -ih "$DATA_DIR"
findmnt -T "$DATA_DIR"
lsblk -f
需要同时关注以下项目:
df -hT:容量、已用空间、可用空间和文件系统类型。df -ih:inode是否耗尽。大量小文件、日志或临时文件可能先耗尽inode。findmnt:确认数据目录是否真的位于预期挂载点,避免数据写到了系统盘。lsblk -f:确认设备、文件系统和挂载关系,不能只看设备名称判断是否使用了目标存储。
如果系统安装了GNU版磁盘工具,可以进一步查看目录占用:
du -xhd1 "$DATA_DIR" 2>/dev/null | sort -h
不要只统计数据库主目录。还应检查备份目录、系统日志目录、临时目录和应用日志目录。如果迁移备份文件也存放在目标服务器,备份文件必须纳入目标盘容量计算。
2. 获取数据库内部的实际大小
PostgreSQL可以按数据库查看数据和索引总量:
SELECT
datname,
pg_size_pretty(pg_database_size(datname)) AS database_size,
pg_database_size(datname) AS database_size_bytes
FROM pg_database
WHERE datistemplate = false
ORDER BY pg_database_size(datname) DESC;
如需定位单个数据库中最大的表和索引,可在目标数据库或源数据库中执行:
SELECT
n.nspname AS schema_name,
c.relname AS object_name,
pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size,
pg_total_relation_size(c.oid) AS total_size_bytes
FROM pg_class AS c
JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r', 'm', 'p')
ORDER BY pg_total_relation_size(c.oid) DESC
LIMIT 20;
MySQL可以先查看各库的表数据和索引大小:
SELECT
table_schema,
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS size_mb
FROM information_schema.tables
GROUP BY table_schema
ORDER BY SUM(data_length + index_length) DESC;
数据库统计值与文件系统占用不一定完全相同。文件系统还可能包含事务日志、临时表、归档日志、错误日志、已删除但仍被进程占用的文件,以及恢复过程中的临时文件。因此,目标端所需空间应按下面的思路计算:
目标端最低所需空间
= 数据与索引实际占用
+ 恢复或重建索引所需临时空间
+ 备份或导出文件占用(如果放在目标端)
+ 迁移期间保留的WAL或二进制日志
+ 操作系统和数据库日志空间
+ 计划增长预留
如果采用逻辑备份,目标端在恢复期间可能同时存在导入文件、已恢复的数据、索引构建临时文件和日志。压缩后的备份文件大小不能直接等同于恢复后的数据库大小,必须以实际恢复测试中的峰值占用为准。
3. 用“可用空间”而不是“总容量”做判断
以下情况说明目标端容量不足或规划过于紧张:
- 目标端只有刚好容纳当前数据的空间,没有备份和恢复临时空间。
- WAL、二进制日志或临时文件增长后,可用空间快速下降。
- 数据库数据目录和备份目录共用同一文件系统。
- inode剩余很少,即使容量还有空余也可能无法创建新文件。
- 恢复过程中出现“no space left on device”或临时目录写入失败。
- 迁移必须删除旧备份才能腾出空间。
遇到这些情况,不应手动删除数据库目录中的文件、WAL文件或二进制日志来“临时腾空间”。正确做法是停止恢复或暂停切换,确认哪些日志受数据库管理,再通过数据库自身的保留策略或增加可用空间处理。
二、备份必须验证“能恢复”,不能只验证“文件存在”
1. 选择最小可用的备份方式
对单次迁移、能够接受维护窗口的场景,逻辑备份通常更容易核对和回滚。下面以PostgreSQL为例:
export APP_DB=appdb
export BACKUP_FILE=/backup/db-migration/appdb.dump
pg_dump \
--format=custom \
--no-owner \
--no-acl \
--file="$BACKUP_FILE" \
"$APP_DB"
test -s "$BACKUP_FILE"
sha256sum "$BACKUP_FILE"
pg_restore --list "$BACKUP_FILE" > "${BACKUP_FILE}.list"
执行前应确认备份目录有足够空间,并使用安全的数据库认证方式,不要把数据库密码直接写在命令行或脚本中。pg_dump完成不代表恢复一定成功,pg_restore --list只能确认备份目录可读,还需要实际恢复测试。
MySQL或兼容实现可以使用一致性导出作为最小方案:
export APP_DB=appdb
export BACKUP_FILE=/backup/db-migration/appdb.sql
mysqldump \
--single-transaction \
--routines \
--events \
--triggers \
--hex-blob \
"$APP_DB" > "$BACKUP_FILE"
test -s "$BACKUP_FILE"
sha256sum "$BACKUP_FILE"
--single-transaction主要适用于事务型表。若数据库中存在非事务型表、特殊存储引擎或需要锁定才能保证一致性的对象,应先确认导出一致性,不能把该参数当作所有表的通用保证。
2. 在目标端进行隔离恢复
恢复测试必须使用目标端的独立数据库名或隔离实例,不能直接覆盖待切换的生产数据库。恢复会写入大量数据,操作前应确认目标端有足够空间,并保留源端原始数据和备份文件。
PostgreSQL示例:
export RESTORE_DB=appdb_restore_check
createdb -T template0 "$RESTORE_DB"
pg_restore \
--exit-on-error \
--no-owner \
--no-acl \
--dbname="$RESTORE_DB" \
"$BACKUP_FILE"
psql "$RESTORE_DB" -c "SELECT current_database(), version();"
psql "$RESTORE_DB" -c "SELECT extname FROM pg_extension ORDER BY extname;"
createdb和pg_restore属于数据库写入操作,只能对确认为空的隔离数据库执行。不要把RESTORE_DB替换成仍在提供生产流量的数据库名。若恢复失败,应保留错误日志,删除或重建隔离数据库前先确认不需要其中的数据,不要用批量删除命令处理生产目录。
MySQL示例:
export RESTORE_DB=appdb_restore_check
mysql -e "CREATE DATABASE \`$RESTORE_DB\`;"
mysql "$RESTORE_DB" < "$BACKUP_FILE"
mysql "$RESTORE_DB" -e "SELECT DATABASE(), VERSION();"
上面的建库和导入操作同样只适用于新建的隔离数据库。若数据库已存在,不应直接导入,否则可能覆盖同名对象或造成对象混杂。
3. 验证恢复结果
恢复成功至少要核对四类结果:
- 对象是否完整:数据库、表、索引、视图、存储过程、触发器、扩展和权限是否符合源端清单。
- 数据是否完整:抽查核心表的记录数、最大主键、最新业务时间和关键状态分布。
- 连接是否正常:使用生产应用账号进行连接测试,检查字符集、时区和默认Schema。
- 写入是否正常:在隔离环境使用应用的测试流程执行一次新增、修改和事务回滚验证。
不要只比较所有表的记录总数。记录数相同不代表数据内容一致,至少应对核心业务表进行抽样校验。对于有序列、自增字段或特殊权限的数据库,还要验证新数据能够继续写入。
如果恢复测试出现以下问题,应在隔离环境修复后再进入迁移窗口:
| 现象 | 常见原因 | 处理方向 |
|---|---|---|
| 找不到扩展或插件 | 目标端未安装对应扩展,或版本不兼容 | 先完成扩展安装和版本匹配 |
| 恢复时权限错误 | 目标端角色、所有者或授权关系不同 | 根据最小权限原则补齐角色和授权 |
| 字符集或排序规则异常 | 源端与目标端默认设置不同 | 显式设置数据库字符集、排序规则和连接参数 |
| 恢复中途空间不足 | 只按备份文件大小估算容量 | 记录峰值占用,重新规划目标存储 |
| 导入成功但应用报错 | 连接参数、时区、Schema或序列未匹配 | 使用应用账号进行完整冒烟测试 |
三、迁移前建立写入监控基线
1. 先看操作系统层面的写入
如果系统已安装sysstat,可以使用以下命令观察磁盘写入:
iostat -xz 1
重点观察目标数据盘对应设备的:
- 写入吞吐量;
- 写请求数量;
await或写入等待时间;- 设备利用率;
- 队列长度;
- 是否出现持续的写入等待。
同时可以查看进程级磁盘活动:
pidstat -d 1
如果命令不存在,应先确认发行版和软件包来源,再安装监控工具,不要在正式切换高峰临时修改生产环境。iostat字段名称可能随工具版本不同而变化,判断时应以实际输出的字段说明为准。
文件系统空间建议与写入监控同时执行:
watch -n 5 'df -hT /var/lib/postgresql; df -ih /var/lib/postgresql'
将路径替换为实际数据目录。如果不确定数据库进程名称或数据盘设备,不要凭进程名猜测,应结合psql、mysql返回的数据目录和findmnt结果确认。
2. 查看数据库内部的累计写入计数
PostgreSQL中的许多统计项是从统计重置后开始累计的,需要连续采样并计算两个时间点之间的差值:
SELECT
now(),
datname,
xact_commit,
xact_rollback,
tup_inserted,
tup_updated,
tup_deleted,
temp_files,
temp_bytes
FROM pg_stat_database
WHERE datname IS NOT NULL;
如果数据库版本提供WAL统计,可以查看WAL记录和字节变化:
SELECT
now(),
wal_records,
wal_fpi,
wal_bytes
FROM pg_stat_wal;
如果当前版本不支持某个列,应先执行以下命令确认版本和可用字段,不要直接套用其他版本的查询:
SELECT version();
\d+ pg_stat_wal
也可以在psql中连续观察:
SELECT
now(),
datname,
xact_commit,
xact_rollback,
tup_inserted,
tup_updated,
tup_deleted,
temp_bytes
FROM pg_stat_database
WHERE datname = 'appdb';
\watch 5
xact_commit、tup_inserted等是累计值,不能把某一次输出直接当作每秒写入量。应记录一段时间内的增量,并分别保存业务低峰、高峰和备份期间的结果。
MySQL可以查询InnoDB日志和数据写入相关计数:
SHOW GLOBAL STATUS
WHERE Variable_name IN (
'Innodb_os_log_written',
'Innodb_data_written',
'Innodb_log_waits',
'Com_insert',
'Com_update',
'Com_delete',
'Threads_connected'
);
其中,Innodb_os_log_written、Innodb_data_written和各类Com_计数通常也是累计值。两次采样的差值可以用来判断写入趋势。Innodb_log_waits持续增加,通常说明日志缓冲或存储写入跟不上事务产生速度,需要结合磁盘等待、日志配置和事务大小继续判断。
3. 采用基线对比,不要只看单个指标
| 观察结果 | 可能说明 | 下一步 |
|---|---|---|
| 数据库写入计数突然增加,磁盘写入同步增加 | 业务批处理、导入任务或定时任务正在运行 | 确认任务时间,避免在异常高峰切换 |
| WAL或二进制日志增长,但业务写入没有明显增加 | 复制、归档、长事务或日志保留异常 | 检查长事务、归档状态和日志保留规则 |
| 磁盘等待升高,CPU利用率不高 | 存储队列、文件系统或后台任务可能成为瓶颈 | 分离备份任务,检查数据盘和日志盘的写入 |
| 可用空间持续下降 | 数据增长、日志积压、临时文件或备份占用空间 | 先查明增长来源,不要手动删除数据库日志 |
| 写入量不高但应用仍然变慢 | 瓶颈可能在锁、CPU、查询计划或连接池 | 不要仅凭NVMe更换判断数据库一定会变快 |
| 迁移恢复阶段写入远高于日常基线 | 索引构建、批量导入或日志刷盘正在集中发生 | 延长维护窗口,重新确认目标端容量和写入承受能力 |
写入监控的目的不是追求某个固定数字,而是发现迁移期间是否偏离源端基线。不同数据库版本、表结构、事务大小和索引数量都会改变写放大,不能用单一的每秒写入量推导所有业务的安全值。
四、按最小方案执行正式迁移
如果业务可以接受维护窗口,可以采用“最终停写、最终备份、目标端恢复、验证后切换”的路径。它比实时复制简单,但维护时间取决于备份和恢复速度,必须先做一次恢复演练。
第一步:冻结变更并确认源端状态
在维护窗口开始前:
- 停止定时导入、批处理和会修改数据库的后台任务;
- 记录源端数据库版本、数据目录、空间使用率和写入基线;
- 确认目标端的数据库服务、挂载点和监控已正常工作;
- 确认备份目录有足够空间,备份文件不会与源端共用唯一存储;
- 保留源端,不要提前卸载数据盘或删除数据库文件。
第二步:停止应用写入,再生成最终备份
先让应用进入维护状态或只读状态,再确认没有新的写事务进入。可以通过应用层连接控制和数据库活动查询双重确认。不要只关闭一个应用进程,因为定时任务、管理脚本或其他服务可能仍在写入。
完成最终备份后,立即执行文件大小、校验和和备份目录清单检查。最终备份必须标记时间和来源,避免误用演练备份。
第三步:在目标端恢复并观察空间
恢复过程中同时观察:
df -hT /目标数据目录
df -ih /目标数据目录
iostat -xz 1
将/目标数据目录替换成目标服务器真实路径。恢复过程中如果空间快速下降、日志持续增长或设备等待异常,应暂停后续切换,先确认是否仍有临时文件、旧备份或索引构建过程占用空间。
第四步:完成数据和应用验证
验证内容至少包括:
- 核心表记录数和关键时间范围;
- 数据库版本、扩展、字符集、时区和默认Schema;
- 应用账号连接;
- 查询、登录、下单或其他核心业务流程;
- 一次受控的写入和事务回滚测试;
- 目标端写入监控是否有异常增长。
如果验证失败,源端仍未承接新写入时,可以直接放弃目标端本次恢复,修复原因后重来。不要为了赶时间跳过验证。
第五步:切换连接并观察目标端写入
切换应用连接参数或内部服务地址后,先观察少量业务请求,再逐步恢复正常流量。切换后重点观察:
- 目标数据库是否出现连接失败;
- 写入计数是否与业务流量匹配;
- WAL或二进制日志是否持续堆积;
- 数据盘可用空间是否异常下降;
- 应用错误日志和事务回滚是否增加;
- 写入等待是否明显偏离迁移前基线。
如果目标端写入正常、关键业务验证通过,并且监控在一段观察窗口内稳定,再结束源端的只读保留期。源端不要在刚切换成功后立即删除,至少应按照既定保留策略保存回退条件和原始备份。
五、失败回滚时要区分“尚未写入”和“已经写入”
切换后目标端还没有生产写入
如果应用尚未向目标端写入,且只是连接、权限或应用启动失败,可以:
- 停止目标端对外提供生产写入;
- 将应用连接恢复到源端;
- 验证源端仍可读写;
- 记录目标端失败原因;
- 修复目标配置后重新演练。
这种回滚相对简单,因为源端仍然是最新数据源。
目标端已经产生生产写入
如果目标端已经接受了新增、修改或删除,不能直接把连接切回源端。这样会丢失目标端的新数据,形成双写分叉。此时应先停止写入,保留目标端现场,根据实际情况选择:
- 导出目标端新增或变更数据,再经过校验后合并到源端;
- 使用预先建立的反向复制或增量同步方案;
- 从目标端备份恢复到新的隔离环境后进行数据比对;
- 如果无法安全合并,则暂停回滚,由数据库管理员制定数据修复方案。
因此,回滚方案必须在切换前写清楚“最后一致时间”和“谁是当前权威库”,而不是只准备一个旧连接地址。
六、什么时候需要升级迁移方案
最小方案适合数据规模可控、能够安排维护窗口、应用可以短时间停止写入的场景。出现以下情况时,应在正式迁移前增加资源或改用更完整的同步方式:
- 最终备份和恢复时间超过可接受维护窗口;
- 源端在备份期间仍必须持续写入;
- 目标端恢复测试中的峰值空间无法满足;
- WAL或二进制日志在业务高峰持续增长,且没有可靠保留空间;
- 只能通过删除旧备份、日志或数据库文件来腾出空间;
- 迁移后写入等待长期高于源端基线;
- 业务要求切换失败后能够保留目标端新写入并快速回退;
- 当前只能人工查看命令输出,无法持续记录空间、写入和备份状态。
升级方向可以按实际缺口选择:为备份和数据库数据使用独立存储、增加持续日志归档、建立增量或实时复制、接入集中式监控、延长观察窗口,或先优化高写入任务和长事务。
只要容量检查、可恢复备份、写入基线和回滚边界都能通过验证,迁移到美国NVMe服务器就有了可判断的基础。若其中任何一项无法验证,优先解决验证问题,而不是仅凭NVMe存储介质推断数据库一定会获得性能提升。