上一篇 下一篇 分享链接 返回 返回顶部

如何在香港服务器中实现 Oracle 数据库从 Windows 迁移到 Ubuntu,避免数据丢失与停机?

发布人:Minchunlin 发布时间:2025-08-25 10:55 阅读量:619


第一次去香港荃湾的机房是上周2的凌晨两点,冷气口像在耳边低语,机柜门“啪”地一声合上,我在KVM前看着那台跑了七年的 Windows Server 上的 Oracle 实例,心里只剩一句话:这次迁到 Ubuntu,不能丢数据,也不能耽误业务。

项目经理把奶茶递给我:“能不停机最好。”我笑了笑,“我尽量做到几分钟级的切换。”这篇文章就是那一夜(其实是三夜)的全过程:怎么选方案、怎么搭环境、怎么一步步把 Windows 上的 Oracle 挪到 Ubuntu 上,同时把风险压扁、把停机时间卡死在最短的窗口里。

关键信息 & 风险声明(看这段能决定你要不要继续)

  • 目标:将 Oracle 数据库(源:Windows) 迁移到 Ubuntu 的香港服务器上,数据零丢失、停机最小化。
  • 现实情况:Oracle 官方支持重点在 Oracle Linux / RHEL / SLES。Ubuntu 不是官方支持的数据库宿主操作系统。
  • 我的做法(强烈推荐):在 Ubuntu 宿主机 上使用 Oracle 官方容器镜像(基于 Oracle Linux) 跑 Oracle Database。这样既能满足“目标是 Ubuntu”,又不违背“数据库运行环境得可支持/可维护”的底线。
  • 停机目标:按本文方案(可重复增量同步 + 一次短暂停机切换),实际项目把业务只读窗口+DNS切换控制在 3–12 分钟。你的数据库规模、对象类型与网络传输带宽会影响这个数字。

适用版本:下文以 Oracle 19c 为例(长期支持,生产更稳),源端可以是 11gR2/12c/18c/19c(跨平台注意端序/块大小/字符集等检查)。

1. 我们的现场:硬件、网络与目标形态

1.1 香港服务器(目标端)硬件与系统参数

项目 配置(实际项目参考)
机房 HK(10Gbps 国际出口,低时延回大陆)
宿主机 OS Ubuntu 22.04 LTS(裸金属)
CPU AMD EPYC 7413(24C/48T),单路
内存 256GB ECC
系统盘 2 × SATA SSD(RAID1,系统/日志)
数据盘 4 × NVMe U.2 3.84TB(mdadm RAID10,或 ZFS RAID10)
网卡 2 × 10GbE(bond,LACP)
时钟 chrony 同步(与 Stratum1/2)
容器 Docker / Podman
文件系统 XFS(数据卷),noatime,I/O 调度 none(NVMe)
监控 node_exporter + oracledb_exporter + Prometheus + Grafana
备份 近端磁盘 + 远端对象存储(每日全量/增量策略)

为何容器:Ubuntu 不是 Oracle DB 的官方支持宿主。用官方 Oracle Linux 基镜像的容器在 Ubuntu 跑,镜像里是 Oracle 支持的用户空间,既能发挥 Ubuntu 宿主的生态便利,也能拿到 Oracle 的已知可运行栈。

1.2 源端(Windows)参数

项目 配置(示例)
OS Windows Server 2016 Standard
Oracle 12.2.0.1 单实例(文件系统)
数据量 1.8 TB(OLTP + 报表混合,峰值 12k TPS)
字符集 AL32UTF8
DB Block Size 8KB
存储 本地 SSD,周末离峰期可到 1.2GB/s 顺序读

2. 迁移路线怎么选?(三种方案对比)

方案 停机 难度 许可/成本 适用场景
Data Pump(expdp/impdp)全量逻辑迁移 高(小时级〜天) 数据量小(<500GB)或能接受长停机
可传输表空间(TTS)+ RMAN 增量滚动(本文详解) 低(分钟级〜十来分钟) 大库、跨平台、可接受短窗口
GoldenGate 双活 极低(秒级) 高(需许可) 对停机极敏感的核心库

我最终选了 TTS + RMAN 增量滚动:稳定、成本可控、停机短。

思路是:先做一次“基线拷贝”,之后周期性增量把源端变更“滚动”到目标;切换窗口把源端表空间只读,打最后一次增量并应用到目标,然后导入元数据、开放读写。

3. 迁移前的健康检查 & 演练(别省)

3.1 跨平台可迁性检查(端序、平台、块大小)

在源端(Windows)检查:

-- 端序/平台
SELECT d.PLATFORM_NAME, d.ENDIAN_FORMAT 
FROM V$TRANSPORTABLE_PLATFORM d 
WHERE UPPER(d.PLATFORM_NAME) LIKE '%LINUX%';

-- 数据库块大小(需与目标兼容,TTS 要一致)
SHOW PARAMETER db_block_size;

-- 字符集与国家字符集(避免导入后乱码)
SELECT parameter, value FROM NLS_DATABASE_PARAMETERS 
WHERE parameter IN ('NLS_CHARACTERSET','NLS_NCHAR_CHARACTERSET');

-- 自包含性检查(可传输表空间要自包含)
EXECUTE DBMS_TTS.TRANSPORT_SET_CHECK('USERS,DATA,INDEX,...', TRUE);
SELECT * FROM TRANSPORT_SET_VIOLATIONS;

要点:

  • Windows x86_64 与 Linux x86_64 端序一致(Little Endian),TTS 可行。
  • db_block_size 多为 8KB,目标必须一致。
  • 对象自包含是 TTS 的红线,出现 TRANSPORT_SET_VIOLATIONS 必须清理(跨表空间依赖、分区子表、物化视图日志等)。

3.2 性能基线(迁前有数,迁后好对比)

用 AWR/Statspack 拉 24h 的基线,记录如下指标(摘录):

指标 峰值 平均 备注
TPS 12,100 3,800 峰值来自报表并发
逻辑读(blocks/s) 4.2M 1.1M  
平均 SQL 响应(ms) 19 8 95p
存储 IOPS(读/写) 210k / 85k 55k / 22k FIO 预估上限更高
网卡吞吐(Gbps) 6.5 1.8 备份/同步窗口更高

4. 在 Ubuntu 宿主搭好“可支持”的 Oracle 目标端

4.1 宿主内核与资源(Ubuntu)

# HugePages (示例按 SGA 128G 计算,8G 预留给 OS 与容器外服务)
echo 'vm.nr_hugepages=65536' | sudo tee /etc/sysctl.d/99-hugepages.conf

# NVMe I/O 调度与挂载
sudo nvme list
# udev 覆盖建议设为 none(多数新内核 NVMe 默认 none)
# XFS 推荐 noatime
echo 'UUID=<data-vol-uuid> /u02 xfs defaults,noatime 0 2' | sudo tee -a /etc/fstab

# ulimit(给 docker 守护 & oracle 容器)
echo '* soft nofile 1048576' | sudo tee -a /etc/security/limits.conf
echo '* hard nofile 1048576' | sudo tee -a /etc/security/limits.conf

4.2 拉取 Oracle 19c 容器并准备目录

注意:生产请使用 Oracle 官方镜像(需登录容器仓库),本文示例用占位名称。

# 数据与归档目录(映射为持久卷)
sudo mkdir -p /u02/oradata /u02/fast_recovery_area /u02/backup /u02/admin
sudo chown -R 54321:54321 /u02  # 让容器内 oracle 用户(54321)有权限

# 拉镜像(示例)
docker pull container-registry.oracle.com/database/enterprise:19.22.0

# 运行(示例 compose)
cat > docker-compose.yml <<'YAML'
services:
  orcl19c:
    image: container-registry.oracle.com/database/enterprise:19.22.0
    container_name: orcl19c
    environment:
      - ORACLE_SID=ORCL
      - ORACLE_PDB=ORCLPDB1
      - ORACLE_PWD=Str0ng-Oracle!
      - ENABLE_ARCHIVELOG=true
      - MEMORY_TARGET=150G
    ulimits:
      nofile:
        soft: 1048576
        hard: 1048576
    volumes:
      - /u02/oradata:/opt/oracle/oradata
      - /u02/fast_recovery_area:/opt/oracle/fast_recovery_area
      - /u02/backup:/backup
      - /u02/admin:/opt/oracle/admin
    network_mode: host   # 或独立网段绑定 1521/5500 端口
    restart: always
YAML

docker compose up -d

4.3 监听/网络与目录对象

容器起来后(首次需几分钟初始化),在容器中确认:

docker exec -it orcl19c bash
lsnrctl status
sqlplus / as sysdba

-- 创建数据泵目录(用于导入元数据)
CREATE OR REPLACE DIRECTORY dpdir AS '/backup/dpdir';
GRANT READ, WRITE ON DIRECTORY dpdir TO system;

5. 迁移流程(TTS + RMAN 增量滚动)——核心步骤

核心思想:基线 + 多轮增量,最后短暂停机只读切换,导入元数据,开库读写。

5.1 规划与“干跑”(Dry Run)

在源端复制一套子集(比如 200GB 的典型业务 schema),按下面的流程先走完一遍,验证脚本、参数、带宽与时间估算。

记录每一步实际耗时,修正切换窗口计划与回滚预案。

5.2 基线数据(Level 0)

源端(Windows)——挑一个业务低谷点,不需要停写:

# 以可传输表空间为单位做 Level 0
rman target /
RUN {
  ALLOCATE CHANNEL c1 DEVICE TYPE DISK;
  BACKUP INCREMENTAL LEVEL=0
    FORMAT 'E:\rman_tts\L0_%U.bkp'
    TABLESPACE USERS, DATA, INDEX;
  RELEASE CHANNEL c1;
}

把 L0 备份文件通过专线/加密隧道传输到目标 /u02/backup/L0/。

目标端(Ubuntu/容器)——转换与恢复到“滚动库”:

rman target /
RUN {
  -- 指定转换平台(Linux x86-64)
  CONVERT FROM PLATFORM 'Microsoft Windows x86 64-bit'
    DATAFILECOPY '/u02/backup/L0_*.bkp'
    TO PLATFORM 'Linux x86 64-bit'
    DB_FILE_NAME_CONVERT
      'E:\ORADATA\ORCL\', '/opt/oracle/oradata/ORCL/';
}

-- 恢复到滚动表空间(aux 方式,或直接将文件放到目标库的数据目录)

说明:也可以采用 RMAN CONVERT TABLESPACE 或 CONVERT DATAFILE 的写法,关键是把 Windows 格式的数据文件转换为 Linux 可用格式。

5.3 周期性增量(Level 1 滚动)

源端(业务照常读写,每隔 30–60 分钟来一轮,视变更量而定):

RUN {
  ALLOCATE CHANNEL c1 DEVICE TYPE DISK;
  BACKUP INCREMENTAL LEVEL=1
    FORMAT 'E:\rman_tts\L1_%d_%T_%U.bkp'
    TABLESPACE USERS, DATA, INDEX;
  RELEASE CHANNEL c1;
}

传到目标 /u02/backup/L1/ 后,目标端应用增量:

RUN {
  -- 将增量应用到已转换的数据文件上(ROLL FORWARD)
  RECOVER FROM '/u02/backup/L1' TABLESPACE USERS, DATA, INDEX;
}

这一步反复执行,直到切换前一刻。每次增量都在缩小最终停机窗口。

5.4 切换窗口(只读 + 最后一增 + 元数据导入)

我们把切换安排在 HKT 02:00,提前把 DNS TTL 调到 60s。

源端(进入只读):

ALTER TABLESPACE USERS READ ONLY;
ALTER TABLESPACE DATA READ ONLY;
ALTER TABLESPACE INDEX READ ONLY;
-- 视情况停写关键队列/任务,或让应用进入只读模式

源端再打一轮 Level 1:

RUN {
  BACKUP INCREMENTAL LEVEL=1
    FORMAT 'E:\rman_tts\L1_FINAL_%U.bkp'
    TABLESPACE USERS, DATA, INDEX;
}

传到目标,目标端应用最后一次增量:

RUN {
  RECOVER FROM '/u02/backup/L1' TABLESPACE USERS, DATA, INDEX;
}

导出源端元数据(只导元数据,速度快):

# Windows 上生成 parfile,例如 C:\exp_tts.par
# DUMPFILE 放共享目录,或先落本地再传

exp_tts.par 示例(只导出 TTS 的元数据):

USERID='/ as sysdba'
TRANSPORT_TABLESPACES=USERS,DATA,INDEX
TRANSPORT_FULL_CHECK=Y
CONTENT=METADATA_ONLY
DIRECTORY=DPDIR
DUMPFILE=tts_meta_%U.dmp
LOGFILE=tts_meta.log
EXCLUDE=STATISTICS

expdp parfile=C:\exp_tts.par

把 tts_meta_*.dmp 拷到目标 /u02/backup/dpdir/。

目标端导入元数据:

-- 确认目标库已将相应数据文件挂载(路径在容器 /opt/oracle/oradata/...)

-- 确保有同名/映射的表空间(或使用 remap)

impdp system/Str0ng-Oracle@localhost:1521/ORCLPDB1 \
  directory=dpdir \
  dumpfile=tts_meta_01.dmp,tts_meta_02.dmp \
  logfile=imp_tts.log \
  transport_datafiles="
/opt/oracle/oradata/ORCL/users01.dbf,
/opt/oracle/oradata/ORCL/data01.dbf,
/opt/oracle/oradata/ORCL/index01.dbf"

导入成功后:

-- 把表空间切回读写
ALTER TABLESPACE USERS READ WRITE;
ALTER TABLESPACE DATA READ WRITE;
ALTER TABLESPACE INDEX READ WRITE;

-- 处理序列(可能要重置至当前最大值)
-- 重新编译无效对象
@?/rdbms/admin/utlrp.sql

-- 统计信息
EXEC DBMS_STATS.GATHER_DATABASE_STATS(estimate_percent=>DBMS_STATS.AUTO_SAMPLE_SIZE, cascade=>TRUE);

切流量:

切换应用连接串(或改 DNS / VIP),监测连接成功与业务指标恢复。

保留源端只读一段时间作为回退点(比如 1–2 小时)。

6. 实操脚本清单(可直接改名套用)

6.1 RMAN(Windows 源)Level 1 增量(批处理)

:: rman_l1.bat
set ORACLE_SID=ORCL
set NLS_DATE_FORMAT=YYYYMMDD_HH24MISS
rman target / log=E:\rman_tts\logs\l1_%date%_%time%.log append <<RMAN
RUN {
  ALLOCATE CHANNEL c1 DEVICE TYPE DISK;
  BACKUP INCREMENTAL LEVEL=1
    FORMAT 'E:\rman_tts\L1_%d_%T_%U.bkp'
    TABLESPACE USERS, DATA, INDEX;
  RELEASE CHANNEL c1;
}
EXIT
RMAN

6.2 目标端应用增量(Linux)

# apply_l1.sh
rman target / <<'RMAN'
RUN {
  CATALOG START WITH '/u02/backup/L1';
  RECOVER TABLESPACE USERS, DATA, INDEX;
}
RMAN

6.3 数据泵元数据导入(Linux)

impdp '"sys/Str0ng-Oracle! as sysdba"' \
  directory=dpdir \
  dumpfile=tts_meta_%U.dmp \
  logfile=imp_tts.log \
  transport_datafiles="/opt/oracle/oradata/ORCL/users01.dbf,/opt/oracle/oradata/ORCL/data01.dbf,/opt/oracle/oradata/ORCL/index01.dbf"

7. 联调、验收与回滚策略

7.1 验收清单(摘录)

  •  会话可连接、主要业务路径 CRUD 正常
  •  序列不回退(关键表 select max(id) 与 sequence.nextval 比对)
  •  物化视图刷新 OK;作业(DBMS_SCHEDULER)在新库按时跑
  •  AWR 对比关键 SQL 的执行计划一致(或更优)
  •  延迟写、提交响应、锁等待指标无异常尖峰
  •  备份策略在目标端已生效(全量/增量/归档清理)

7.2 回滚预案(窗口内极简)

回滚触发条件:导入失败、关键 SQL 性能不可接受、应用连接异常 > 5 分钟。

回滚操作:

  • DNS/VIP 指回源端;
  • 源端表空间改回读写:ALTER TABLESPACE ... READ WRITE;
  • 源端继续提供服务;
  • 复盘问题,修正后择期再切。

8. 现场“坑”与解决(真遇到的)

容器内 oracle 用户 UID 与宿主卷权限不一致

现象:导入/归档写入报 ORA-27040 权限错误。

处理:chown -R 54321:54321 /u02/*,并固定 compose 中的 user: "54321:54321"(如镜像支持)。

TRANSPORT_SET_VIOLATIONS 报跨表空间依赖

现象:部分分区索引、MV 日志跨到了 SYSTEM/UNDOTBS。

处理:重建到目标 TTS 范围内的表空间,或在切换前拆分对象、清理垃圾对象。

NLS 时区文件版本不一致

现象:时间函数/作业异常。

处理:对齐 $ORACLE_HOME/oracore/zoneinfo 版本,执行 ALTER DATABASE SET TIME_ZONE(如需),并重启。

XFS mount 忘了 noatime

现象:大量小读时 iowait 偶发升高。

处理:改挂载参数,配合 NVMe none 调度,降低元数据写放大。

RMAN 转换吞吐不达标

处理:开启 多通道并行 与 多段备份:

RUN {
  ALLOCATE CHANNEL c1 DEVICE TYPE DISK MAXPIECESIZE 64G;
  ALLOCATE CHANNEL c2 DEVICE TYPE DISK MAXPIECESIZE 64G;
  BACKUP AS COPY INCREMENTAL LEVEL=1
    SECTION SIZE 32G
    TABLESPACE USERS,DATA,INDEX;
}

应用端连接串缓存

现象:应用容器没有刷新 DNS。

处理:用 VIP/Keepalived 或中间层(HAProxy/MaxScale 类)统一出口,避免客户端 DNS 缓存不可控。

9. 迁前后关键指标对比(实测样例)

指标 迁前(Windows 源) 迁后(Ubuntu 宿主 + Oracle 容器) 变化
主库峰值 TPS 12,100 12,800 +5.8%
平均 SQL 响应 p95 19ms 16ms -15.7%
存储顺序读(GB/s) 1.2 2.3 +91%(NVMe RAID10)
写入 IOPS 85k 120k +41%
切换停机(可见层) 11 分钟 达成目标

这些提升大多来自 NVMe 带宽/并发、I/O 调度优化 与 容器镜像的用户空间匹配(Oracle Linux)。

10. FAQ:你可能会问

一定要容器吗?

不硬性,但在 Ubuntu 上容器是兼顾“目标是 Ubuntu”和“Oracle 可运行栈”的最稳妥方案。纯原生在 Ubuntu 上装 Oracle 可行性差、维护风险高。

能做到零停机吗?

真正意义的“零”需要 GoldenGate 级别的双活/级联切换与许可证预算。本文 TTS+增量能把窗口压到几分钟,对多数业务已足够。

非 CDB → CDB/PDB 怎么办?

可以在目标端建 CDB,采用 TTS 到 PDB 中,或考虑 跨平台可传输 PDB(视版本而定)。逻辑要点相同:自包含、元数据导入、读写切换。

11. 完整执行顺序(Checklist 版)

  • 规划对象范围(表空间级),清理跨依赖,自包含检查通过。
  • 建立目标端:Ubuntu 宿主 → Oracle 容器,调内核/IO/ulimit。
  • Dry Run(子集库)验证脚本与耗时。
  • Level 0 基线备份 → 传输 → 转换/恢复。
  • 多轮 Level 1 增量滚动,验证可应用性与耗时。
  • 切换前 48h:DNS TTL 调小;冻结变更窗口;通知业务。
  • 切换:源端表空间只读 → 最后一轮增量 → 目标导入元数据 → 表空间读写。
  • 切流量(DNS/VIP),压测 & 验收清单全绿。
  • 监控观察期,保留源端只读回退点 1–2 小时。
  • 收尾:统计信息、备份策略切换、AWR 基线与文档归档。

12. 结尾:从机房出来,天色刚亮

那天清晨,机房走廊尽头的窗子透了点灰蓝色的光。

我把最后一个勾打在验收清单上,应用的交易曲线在 Grafana 上恢复成熟悉的波形。项目经理在门口问我:“就这?”

我说:“就这。数据一行没丢,业务停了 11 分钟。”

迁移不是魔法,靠的是提前演练、脚本化和细节不放过。你按这篇文章的顺序走下来,哪怕机房风再大,心也能定住。

附:风险提示与建议

生产环境请优先考虑 Oracle 19c(LTS)或官方支持的版本组合。

若业务对停机极端敏感且预算允许,评估 GoldenGate。

不要跳过 Dry Run;不要忽略 TRANSPORT_SET_VIOLATIONS;不要相信“最后再补统计信息”这种侥幸。

把 回滚预案写成脚本,像做一次“逆迁移演练”。

目录结构
全文