SheepDog1998

博客园 首页 新随笔 联系 订阅 管理
# 迁移操作手册

这份手册按执行顺序排列,每一步都写清楚三件事:在哪台机器上、开什么窗口、敲什么命令。不用批处理,全部手工执行,遇到问题随时停下来问。

## 环境速查

| 角色 | 地址 | 系统 | PG 工具路径 |
| --- | --- | --- | --- |
| 源库 | `10.62.204.32:5432` | Windows Server | `D:\pushServer\postgresql\bin` |
| 目标库 | `10.62.210.67:5432` | Windows Server | `E:\swtools\PostgreSQL\12\bin` |
| Java 应用 | `10.62.210.40` | 麒麟 Linux | 与数据库分离部署 |

**盘符注意:** 目标机 `10.62.210.67` **只有 E 盘,没有 D 盘**。所以本手册里所有工作目录都在 `E:\`(`E:\db_backup`、`E:\migration`)。上面表格里那个 `D:\pushServer\postgresql\bin` 是源机 `10.62.204.32` 的 PG 路径,那台机有 D 盘,不要跟着改。

库名对照,这里最容易搞错,每条命令的 `-d` 参数都要看准:

```
源机 10.62.204.32
  gansueqwin        4248 MB   本次要迁的就是它
  gansueqwin_new    5189 MB   老版本,不迁,别碰

目标机 10.62.210.67
  gansueq           6667 MB   现在线上用的旧库,第 2 步改名成 gansueq_old 保留
  gansueqwin        5519 MB   历史数据,第 2 步改名成 gansueqwin_hist,只保留不用
  gansueq                     迁移后的新库,第 4 步新建
```

数据库口令统一是 `postgres2024`,用户名 `postgres`。

## 全流程一览

```
不停机(提前做,能做完全部做完)
  阶段一  第 1 步   备份目标机旧库
          第 2 步   从源库导出
          第 3 步   本地用 pro 启动一次(可选但建议)

停机窗口开始
  阶段二  第 4 步   停应用
          第 5 步   旧库改名让位
          第 6 步   建新库
          第 7 步   还原
          第 8 步   修序列
          第 9 步   数据校验
          第 10 步  改 profile 并打包部署
          第 11 步  启动与验收
停机窗口结束

出问题  第 12 步   回滚
```

阶段一全部不需要停服,`pg_dump` 是在线操作。把这些提前做完,实际停机只从第 4 步开始。

## 阶段一:不停机准备

### 第 1 步:备份目标机旧库

**在哪:** 目标机 `10.62.210.67`,开一个 CMD 窗口。

**为什么先做:** 备份 6667 MB 要十几分钟到半小时。此刻应用还在跑、库还叫 `gansueq`,先把备份导好,停机时间就不用算这一段。

设置工具路径。**这个变量每开一个新 CMD 窗口都要重设一次**,后面每一步都在用:

```bat
set PGBIN=E:\swtools\PostgreSQL\12\bin
set PGPASSWORD=postgres2024
```

确认工具版本,应显示 `12.x`:

```bat
"%PGBIN%\pg_dump" --version
```

建目录并导出:

```bat
mkdir E:\db_backup

"%PGBIN%\pg_dump" -h 10.62.210.67 -U postgres -d gansueq -Fc -f E:\db_backup\gansueq_backup.dump
```

这条会跑十几分钟,没有进度显示,等它回到命令提示符即可。

验证备份文件没损坏。这步不要省,备份坏了而不自知是很常见的事故:

```bat
"%PGBIN%\pg_restore" -l E:\db_backup\gansueq_backup.dump | more

dir E:\db_backup

certutil -hashfile E:\db_backup\gansueq_backup.dump SHA256
```

`pg_restore -l` 能列出一大串对象清单就说明文件是好的。按 `Q` 退出 `more`。把那个 SHA256 值记下来。

**做完这步告诉我一声**,我帮你确认输出是否正常。

### 第 2 步:从源库导出

**在哪:** 还是目标机 `10.62.210.67` 上的 CMD 窗口,远程连源库拉数据。

**为什么在目标机做:** 省一次文件传输,而且导出和还原用同一套 PG 12 工具,版本天然匹配。

先确认要导的库对不对。源实例里有两个名字相近的库:

```bat
"%PGBIN%\psql" -h 10.62.204.32 -U postgres -d postgres -c "SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database WHERE datname LIKE 'gansueq%';"
```

应该看到 `gansueqwin` 4248 MB 和 `gansueqwin_new` 5189 MB。**要导的是 `gansueqwin`**,没有 `_new` 后缀那个。

再核一次表数量:

```bat
"%PGBIN%\psql" -h 10.62.204.32 -U postgres -d gansueqwin -c "SELECT table_schema, count(*) FROM information_schema.tables WHERE table_schema IN ('public','mapserver_data') AND table_type='BASE TABLE' GROUP BY 1;"
```

应返回 `mapserver_data 88` 和 `public 210`。数字对得上再往下走,对不上先停下来问我。

`table_type='BASE TABLE'` 这个条件不能省。不加的话视图也会被算进去,`public` 会返回 214 而不是 210,看着像凭空多了 4 张表。采集基线用的就是带这个过滤的口径。

开始导出。先导结构,快,几秒钟:

```bat
mkdir E:\migration

"%PGBIN%\pg_dump" -h 10.62.204.32 -p 5432 -U postgres -d gansueqwin --schema-only --no-owner --no-privileges -f E:\migration\schema.sql
```

再导全量,这条要十几分钟:

```bat
"%PGBIN%\pg_dump" -h 10.62.204.32 -p 5432 -U postgres -d gansueqwin --no-owner --no-privileges -Fc -f E:\migration\full.dump
```

`--no-owner --no-privileges` 是刻意加的,源库的用户和权限在目标机上未必存在,去掉能避免还原时一堆 role 报错。

验证:

```bat
dir E:\migration

"%PGBIN%\pg_restore" -l E:\migration\full.dump | more
```

**重要提醒:** 这一步导完之后,源库若还有新数据写入,那部分不会进新库。记下导出完成的时刻。如果源系统数据变化不频繁(从行数看是这样,`t_100_real_earthquake` 才 17475 行),影响很小;否则就把这一步挪到第 4 步停应用之后再做。

### 第 3 步:本地用 pro 启动一次(建议做)

**在哪:** 你自己的开发机,IDE 里。

**做什么:** 把 `application.yml` 的 `active` 临时改成 `pro`,启动一次看能不能起来。

已经实测过 `pro` 的配置是完整的,所以这步大概率一次通过。做它的意义是提前暴露意外,别在停机窗口里试错。

起不来的话把报错发我。起来了就把 `active` 改回去,等第 10 步再正式改。

## 阶段二:停机窗口

从这里开始系统不可用。前面阶段一做完的话,这一段大约 40 分钟到 1 小时。

### 第 4 步:停应用

**在哪:** 麒麟 `10.62.210.40`,SSH 登录。

**应用目录:** `/opt/server-jar/ganEarthquake`

**先读这段再动手:应用是用看门狗脚本启动的,直接 kill Java 进程会被自动拉起。**

`start_gansuEq.sh` 的主体是这样:

```bash
while true; do
  java $JAVA_OPTS -jar $APP_NAME
  ...
  sleep 5
done
```

Java 一退出,脚本睡 5 秒就重新拉起。所以必须**先停脚本、再停 Java**,否则会出现库改到一半、新进程连上不存在的库的情况。

先看清两个进程:

```bash
ps -ef | grep -E "earthquake|start_gansuEq" | grep -v grep
```

会看到两行(PID 每次不同,按实际的来):

```
root  1359659  ...  /bin/bash /opt/server-jar/ganEarthquake/start_gansuEq.sh   ← 父,看门狗脚本
root  1726708  1359659  ...  java -Dfile.encoding=UTF-8 ... -jar earthquake... ← 子,Java 进程
```

**把这两个 PID 记下来。** 然后先父后子地停:

```bash
kill <脚本的PID>
kill <Java的PID>
```

确认两个都没了:

```bash
ps -ef | grep -E "earthquake|start_gansuEq" | grep -v grep
```

**必须没有任何输出。** 如果 Java 进程又冒出来了,说明脚本没停干净,先确认脚本进程:

```bash
ps -ef | grep start_gansuEq | grep -v grep
```

把它 `kill -9` 掉,再处理 Java。

顺带说明:平时你是用 Ctrl+C 停的,那样能同时结束脚本和 Java(同一进程组)。但当前进程挂在 8 月 20 日就断开的 `pts/5` 上,回不到那个终端,所以这次用 kill。

**记录停机开始时间。**

**记录停机开始时间。**

### 第 5 步:旧库改名让位

**在哪:** 目标机 `10.62.210.67`,CMD 窗口。

改名是元数据操作,秒级完成,数据一行都不动。改完旧库依然完好,只是换了名字。

```bat
set PGBIN=E:\swtools\PostgreSQL\12\bin
set PGPASSWORD=postgres2024

"%PGBIN%\psql" -h 10.62.210.67 -U postgres -d postgres
```

进入 psql 后,提示符变成 `postgres=#`。下面的 SQL 一条一条执行:

```sql
\l+
```

先看清现有库名和体积。确认 `gansueq` 和 `gansueqwin` 都在。

```sql
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'gansueq' AND pid <> pg_backend_pid();
```

踢掉旧库的活动连接,改名要求无连接。如果第 4 步应用真停干净了,这条通常返回空。

```sql
ALTER DATABASE gansueq RENAME TO gansueq_old;
```

这就是你的回滚退路,别删它。

```sql
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'gansueqwin' AND pid <> pg_backend_pid();

ALTER DATABASE gansueqwin RENAME TO gansueqwin_hist;
```

目标机这个 `gansueqwin` 是历史数据,你说过可改名不可删。改名是因为它和源库同名,后面命令里两个 `gansueqwin` 太容易搞混。

```sql
\l
```

此时应该看到 `gansueq_old` 和 `gansueqwin_hist`,**没有 `gansueq`**。确认后退出:

```sql
\q
```

### 第 6 步:建新库

**在哪:** 同一个 CMD 窗口。

```bat
"%PGBIN%\psql" -h 10.62.210.67 -U postgres -d postgres
```

```sql
CREATE DATABASE gansueq
  WITH ENCODING 'UTF8'
       LC_COLLATE 'Chinese (Simplified)_China.936'
       LC_CTYPE 'Chinese (Simplified)_China.936'
       TEMPLATE template0;
```

那两个 `LC_` 参数必须显式写,不是可选项。采集确认两边 collate 都是这个值,默认 template1 未必是,不写会导致中文排序和源库不一致。

```sql
\c gansueq
```

切到新库,提示符会变成 `gansueq=#`。注意后面几条必须在新库里执行。

```sql
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS postgis_raster;
CREATE EXTENSION IF NOT EXISTS postgis_topology;
```

三个都要装。后两个是采集查出来的:源库有、目标库只装了 `postgis`。目标机这三个都是「可用未安装」状态,直接建就行,不用装软件。

```sql
SELECT postgis_full_version();
```

应显示 `3.4.2`,与源库一致。

```sql
SELECT extname, extversion FROM pg_extension ORDER BY extname;
```

应返回正好这四行:

```
plpgsql          1.0
postgis          3.4.2
postgis_raster   3.4.2
postgis_topology 3.4.2
```

```sql
CREATE SCHEMA IF NOT EXISTS mapserver_data;

\q
```

### 第 7 步:还原

**在哪:** 同一个 CMD 窗口。

```bat
"%PGBIN%\pg_restore" -h 10.62.210.67 -U postgres -d gansueq --no-owner --no-privileges --jobs=4 --verbose E:\migration\full.dump > E:\migration\restore.log 2>&1
```

这条是整个流程最慢的一步,十几到几十分钟。输出全部写进日志文件,屏幕上不会有东西,等它回到提示符。

`--jobs=4` 按目标机 CPU 核数调,核多可以加大。

还原完了看错误。**会有报错,而且大部分是正常的**:

```bat
findstr /c:"error" E:\migration\restore.log | find /c /v ""
```

这条给出错误总数。然后过滤掉预期内的,看剩下什么:

```bat
findstr /c:"error" E:\migration\restore.log | findstr /v "already exists" | more
```

可以忽略的报错:

```
extension "postgis" already exists          已手动装了
type "geometry" already exists               PostGIS 自带
relation "spatial_ref_sys" already exists    PostGIS 自带
schema "mapserver_data" already exists       已手动建了
role "xxx" does not exist                    加了 --no-owner
could not access file "$libdir/pgrouting"    源库残留野生扩展,业务零引用
could not access file "$libdir/ogr_fdw"      同上
function _pgr_xxx already exists             同上
```

那几个 `$libdir` 报错解释一下:源库里残留了一批 C 函数,来自从未正式登记的扩展(pgRouting 132 个、fuzzystrmatch、address_standardizer、ogr_fdw)。我在全项目 1177 个文件里搜过这些函数名,零引用,业务完全不依赖,放心忽略。

需要停下来排查的:

```
function xxx does not exist      可能 PostGIS 版本不匹配
column xxx does not exist        结构还原不完整
no space left                    磁盘不足
```

**过滤后如果还剩不少错误,先别继续,把日志发我。**

### 第 8 步:修序列

**在哪:** 同一个 CMD 窗口。

**为什么重要:** 项目里 106 处 `IdType.AUTO` 全靠序列生成主键。序列没跟过来,新增数据就会主键冲突。这是最容易漏、漏了必出事的一步。

两边各导一份序列值:

```bat
"%PGBIN%\psql" -h 10.62.204.32 -U postgres -d gansueqwin -t -A -F "," -c "SELECT schemaname||'.'||sequencename, last_value FROM pg_sequences ORDER BY 1" > E:\migration\seq_source.csv
```

```bat
"%PGBIN%\psql" -h 10.62.210.67 -U postgres -d gansueq -t -A -F "," -c "SELECT schemaname||'.'||sequencename, last_value FROM pg_sequences ORDER BY 1" > E:\migration\seq_target.csv
```

比对,**没有输出就是一致**,这步就过了:

```bat
fc E:\migration\seq_source.csv E:\migration\seq_target.csv
```

顺手核对几个数值最大的,肉眼看一下:

```bat
findstr "t_100_distance_info_id_seq t_100_economic_loss_result_id_seq_a sm_seq_datasource" E:\migration\seq_target.csv
```

对照基线:

```
t_100_distance_info_id_seq              298292
t_100_economic_loss_result_id_seq_a     279160
t_100_assessment_yxc_town_id_seq0       106216
t_100_report_record_seq_3                48941
t_100_five_influence_field_id_seq        25157
sm_seq_datasource                          268
```

`sm_seq_datasource` 是 SuperMap 自己的序列,最容易被忽略,漏了会导致新建数据集时 ID 冲突。

有三个序列 `last_value` 是空的(从未使用过),保持默认就行不用管:`hibernate_sequence`、`t_100_influence_param_id_sequence`、`t_100_person_dept_id_sequence`。

**如果 `fc` 报出差异**,进新库执行下面这条。它会按每张表实际最大 id 生成修复语句,比照搬源库值更稳妥:

```bat
"%PGBIN%\psql" -h 10.62.210.67 -U postgres -d gansueq
```

```sql
SELECT 'SELECT setval(''' || quote_ident(s.schemaname) || '.' || quote_ident(s.sequencename) || ''', '
       || 'COALESCE((SELECT MAX(' || quote_ident(a.attname) || ') FROM '
       || quote_ident(n.nspname) || '.' || quote_ident(c.relname) || '), 1), true);' AS fix_sql
FROM pg_sequences s
JOIN pg_class sc ON sc.relname = s.sequencename
JOIN pg_depend d ON d.objid = sc.oid AND d.deptype = 'a'
JOIN pg_class c ON c.oid = d.refobjid
JOIN pg_namespace n ON n.oid = c.relnamespace
JOIN pg_attribute a ON a.attrelid = c.oid AND a.attnum = d.refobjsubid
ORDER BY 1;
```

它输出的是一堆 `SELECT setval(...)` 语句。把结果整段复制出来,粘回 psql 执行一遍即可。

### 第 9 步:数据校验

**在哪:** 同一个 CMD 窗口。

**9.1 表清单比对**

```bat
"%PGBIN%\psql" -h 10.62.204.32 -U postgres -d gansueqwin -t -A -c "SELECT n.nspname||'.'||c.relname FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace WHERE c.relkind='r' AND n.nspname IN ('public','mapserver_data') ORDER BY 1" > E:\migration\tables_source.txt
```

```bat
"%PGBIN%\psql" -h 10.62.210.67 -U postgres -d gansueq -t -A -c "SELECT n.nspname||'.'||c.relname FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace WHERE c.relkind='r' AND n.nspname IN ('public','mapserver_data') ORDER BY 1" > E:\migration\tables_target.txt
```

```bat
fc E:\migration\tables_source.txt E:\migration\tables_target.txt
```

无差异即通过。源库 public 有 205 张表、mapserver_data 88 张。

**9.2 大写表名确认**

这个必须查。PG 会把未加引号的大写标识符折叠成小写,一旦折叠了业务查询会报表不存在。

```bat
"%PGBIN%\psql" -h 10.62.210.67 -U postgres -d gansueq -c "SELECT relname FROM pg_class WHERE relname IN ('mapData_gansu','mapData_qgdc','mapData_qgdc_1','mapdata_rkwgNEW','mapdata_qgxjDZM','t_100_personnel_Loss_param');"
```

**应返回 6 行。** 少了就说明大小写被折叠了,需要改回来,例如:

```sql
ALTER TABLE mapdata_gansu RENAME TO "mapData_gansu";
```

**9.3 实测那条依赖大写表名的查询**

这条 SQL 决定「震中是否在甘肃省内」,是核心业务判断,必须实测。用兰州坐标,**应返回 `t`**:

```bat
"%PGBIN%\psql" -h 10.62.210.67 -U postgres -d gansueq -c "SELECT ST_DWithin(ST_SetSRID(ST_MakePoint(103.8, 36.06), 4326), (SELECT geom FROM \"mapData_gansu\" WHERE province='甘肃省'), 0);"
```

返回 `f` 或报错都要停下来查。

**9.4 关键表行数**

```bat
"%PGBIN%\psql" -h 10.62.210.67 -U postgres -d gansueq -c "SELECT 't_100_real_earthquake' t, count(*) FROM t_100_real_earthquake UNION ALL SELECT 't_100_report_record', count(*) FROM t_100_report_record UNION ALL SELECT 't_100_influence_field', count(*) FROM t_100_influence_field UNION ALL SELECT 't_100_assessment_record', count(*) FROM t_100_assessment_record UNION ALL SELECT 't_100_user', count(*) FROM t_100_user UNION ALL SELECT 't_100_dict_data', count(*) FROM t_100_dict_data UNION ALL SELECT 't_100_setting_items', count(*) FROM t_100_setting_items;"
```

对照基线(采集于 2026-08-26 12:06):

```
t_100_real_earthquake      17475
t_100_report_record        45544
t_100_influence_field       8456
t_100_assessment_record     2082
t_100_user                   123
t_100_setting_items          157
t_100_dict_data               16
```

略多于基线是正常的(导出前源库仍有写入)。**关键是不能少于基线。**

### 第 10 步:改 profile 并打包部署

**在哪:** 先在你的开发机,然后传到麒麟。

**10.1 改 active(开发机)**

编辑 `earthquakeEemergencyGSServerAutoProcess\src\main\resources\application.yml` 第 20 行:

```yaml
spring:
  profiles:
    active: pro
```

原来是 `prowin`。**这个文件会被打进 jar,所以必须打包前改。**

`application-pro.yml` 不用动,已经核实过里面的配置是对的。

**10.2 打包(开发机)**

```bat
cd /d E:\code\earthquakeEemergencyGS\earthquakeEemergencyGSServerAutoProcess

mvn clean package -DskipTests
```

产物是 `target\earthquakeEemergencyGS-1.0-SNAPSHOT.jar`,约 289 MB。

确认打进去的 profile 是对的:

```bat
cd /d E:\code\earthquakeEemergencyGS\earthquakeEemergencyGSServerAutoProcess\target

jar xf earthquakeEemergencyGS-1.0-SNAPSHOT.jar BOOT-INF/classes/application.yml

powershell -NoProfile -Command "Get-Content -Encoding UTF8 'BOOT-INF\classes\application.yml' | Select-String 'active'"
```

应该看到 `active: pro` 那行**前面没有 `#`**,其余三行(dev / test / prowin)都是注释状态。

这里不要用 `findstr`。该文件里有 UTF-8 中文注释,CMD 按 GBK 解析会报 `FINDSTR: 写入错误` 并把输出截断,看起来像 profile 没改对,其实是控制台编码问题。

算校验和,传输后要比对:

```bat
certutil -hashfile earthquakeEemergencyGS-1.0-SNAPSHOT.jar SHA256
```

**10.3 备份麒麟上的旧 jar(麒麟)**

SSH 登录麒麟 `10.62.210.40`。应用目录是 `/opt/server-jar/ganEarthquake`:

```bash
cd /opt/server-jar/ganEarthquake
ls -lh *.jar
```

这个目录里有九个同名 jar 靠后缀区分,当前跑的是没有后缀的 `earthquakeEemergencyGS-1.0-SNAPSHOT.jar`(1 月 12 日打的)。**先确认它没被动过,再改名。**

旧 jar **原地保留别删**,按目录里现有的命名习惯加个后缀:

```bash
mv earthquakeEemergencyGS-1.0-SNAPSHOT.jar earthquakeEemergencyGS-1.0-SNAPSHOT-bk-20260827.jar
ls -lh *.jar
```

确认改名后目录里已经没有无后缀的那个 jar 了,避免新 jar 传上来时覆盖掉旧的。

**10.4 传新 jar 并校验(麒麟)**

用你惯用的方式传(WinSCP、scp 都行),目标目录 `/opt/server-jar/ganEarthquake`。传完在麒麟上算校验和:

```bash
cd /opt/server-jar/ganEarthquake
sha256sum earthquakeEemergencyGS-1.0-SNAPSHOT.jar
```

**和 10.2 里 `certutil` 输出的值比对,必须完全一致。** 289 MB 的文件传输出错不算罕见,这步别省。

### 第 11 步:启动与验收

**在哪:** 麒麟 `10.62.210.40`。

还是用原来的 `start_gansuEq.sh`,但这次把输出重定向到文件。以前脚本没重定向,日志跟着终端走,断开就没了,出问题查不了:

```bash
cd /opt/server-jar/ganEarthquake
nohup ./start_gansuEq.sh > app_20260827.log 2>&1 &
```

盯着日志看启动过程:

```bash
tail -f app_20260827.log
```

**看这几个关键点:**

```
The following profiles are active: pro      profile 对了
HikariPool-1 - Start completed              数据库连上了
Tomcat started on port(s): 2023             端口起来了
Started EarthquakeEmergencyGS in xx seconds 启动成功
```

看到最后一行就是起来了,按 `Ctrl+C` 退出 tail(这只是退出看日志,不会停掉应用,因为是 nohup 起的)。

**如果启动失败,先停脚本再排查。** 脚本是 `while true` 循环,Java 退出后它会等 5 秒再拉一次,日志里会反复出现:

```
Service exited, exit code: 1
Restarting in 5 seconds...
```

看到这个别让它转下去,几分钟就能把日志刷到几百兆。先按 `Ctrl+C` 退出 tail,然后:

```bash
ps -ef | grep -E "earthquake|start_gansuEq" | grep -v grep
kill <脚本的PID>
kill <Java的PID>
```

停干净之后再抓报错。日志已经被重试刷了很多遍,只看第一轮就够:

```bash
grep -i "could not resolve placeholder" app_20260827.log | head -5

grep -i "APPLICATION FAILED TO START" -A 20 app_20260827.log | head -40
```

把输出发我。

**验收检查:**

```bash
ss -lntp | grep 2023
```

端口在监听。然后确认连的是新库:

```bash
grep -i "gansueq" app_20260827.log | head
```

应该能看到 `10.62.210.67:5432/gansueq`,不是 `gansueqwin`。

接下来在浏览器里走一遍业务,这几项最能覆盖风险:

```
1. 登录(验证 t_100_user 数据和 token 配置)
2. 打开地震列表(验证 t_100_real_earthquake,17475 条)
3. 触发一次评估(验证大写表名那条 ST_DWithin 查询、序列自增)
4. 生成一份报告 PDF(验证 SuperMap 出图、Aspose 转 PDF、中文字体)
5. 看地图页面(验证 SuperMap 工作空间数据源指向)
```

第 3 项和第 4 项最关键。第 3 项会同时踩到序列和大写表名两个风险点;第 4 项能验证 SuperMap 和 PDF 字体。

**第 5 项如果地图不出图**,大概率是 SuperMap 工作空间 `.smwu` 里的 PostGIS 数据源还指向旧库。这是已知风险点,处理办法见 `迁移方案.md` 第 8 步。

**记录停机结束时间。**

## 阶段三:出问题时回滚

### 第 12 步:回滚

**什么时候回滚:** 启动失败且十几分钟内定位不到原因,或者核心业务(登录、地震列表、评估)不可用。数据细节问题不用回滚,可以上线后修。

**关键原则:代码和数据库必须一起退。** 新 jar 配旧库、旧 jar 配新库这两种组合都可能起不来。

回滚一共两件事,都很快:

**12.1 数据库改回来(目标机 CMD)**

```bat
set PGBIN=E:\swtools\PostgreSQL\12\bin
set PGPASSWORD=postgres2024

"%PGBIN%\psql" -h 10.62.210.67 -U postgres -d postgres
```

```sql
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname IN ('gansueq','gansueq_old') AND pid <> pg_backend_pid();

ALTER DATABASE gansueq RENAME TO gansueq_new_failed;

ALTER DATABASE gansueq_old RENAME TO gansueq;

\l
\q
```

这样旧库又叫回 `gansueq` 了。**新库改名保留,别删** —— 失败原因还要查。

**12.2 换回旧 jar(麒麟)**

先把看门狗脚本和 Java 都停掉,父进程在前:

```bash
ps -ef | grep -E "earthquake|start_gansuEq" | grep -v grep
kill <脚本的PID>
kill <Java的PID>
ps -ef | grep -E "earthquake|start_gansuEq" | grep -v grep
```

最后一条必须没有输出。脚本还活着的话,你换完 jar 它会自动把新 jar 又拉起来。

然后换回旧 jar:

```bash
cd /opt/server-jar/ganEarthquake
mv earthquakeEemergencyGS-1.0-SNAPSHOT.jar earthquakeEemergencyGS-1.0-SNAPSHOT-new-failed-20260827.jar
mv earthquakeEemergencyGS-1.0-SNAPSHOT-bk-20260827.jar earthquakeEemergencyGS-1.0-SNAPSHOT.jar
ls -lh earthquakeEemergencyGS-1.0-SNAPSHOT.jar
```

确认它是 1 月 12 日那个 290M 的文件,再启动:

```bash
nohup ./start_gansuEq.sh > app_rollback_20260827.log 2>&1 &
tail -f app_rollback_20260827.log
```

旧 jar 里 `active` 是 `prowin`,指向 `10.62.204.32/gansueqwin`(源库),源库一直没动过,所以能起来。

注意这里有个细节:旧 jar 配的是**源库**而不是目标机的库。如果你希望旧 jar 连回目标机那个刚改名回来的 `gansueq`,得用命令行参数覆盖:

这种情况不能用 `start_gansuEq.sh`,脚本里的启动命令是写死的,加不了参数。直接手起,把脚本的 JAVA_OPTS 照抄过来:

```bash
cd /opt/server-jar/ganEarthquake
nohup java -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Xms16g -Xmx16g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar earthquakeEemergencyGS-1.0-SNAPSHOT.jar --spring.datasource.url=jdbc:postgresql://10.62.210.67:5432/gansueq > app_rollback_20260827.log 2>&1 &
```

这样起来没有自动重启保护,回滚稳定后记得改回脚本方式。

**回滚后确认:**

```bash
grep "profiles are active" app_rollback_20260827.log
ss -lntp | grep 2023
```

然后浏览器里登录一下,确认旧系统恢复。

## 观察期

上线后一周内别删任何东西:

```
目标机  gansueq_old                        旧库,随时可回滚
        gansueqwin_hist                    历史数据,永久保留
        E:\db_backup\gansueq_backup.dump   备份文件
        E:\migration\full.dump             源库导出
麒麟    earthquakeEemergencyGS-1.0-SNAPSHOT-bk-20260827.jar   旧 jar
        app_20260827.log                   本次启动日志
源机    gansueqwin                         源库,本次只读不改
```

一周后系统稳定,可以清理 `E:\migration\full.dump` 释放空间,其余建议长期保留。

## 命令速查卡

打印出来放手边。

**每开一个新 CMD 窗口先跑这两条:**

```bat
set PGBIN=E:\swtools\PostgreSQL\12\bin
set PGPASSWORD=postgres2024
```

**psql 里常用的元命令:**

```
\l          列出所有库(带体积用 \l+)
\c 库名     切换库
\dt         列出当前库的表
\q          退出
```

**关键数字对照:**

```
源库 gansueqwin      4248 MB   public 210 表   mapserver_data 88 表
目标旧库 gansueq     6667 MB
PostGIS 版本         3.4.2(两边一致)
PG 版本              12.17(两边一致)
大写表名             应查到 6 个
```

**三个库名别搞混:**

```
gansueqwin        源机,要迁的
gansueqwin_new    源机,不迁
gansueqwin_hist   目标机,历史数据
```

## 遇到问题怎么问我

把这三样发我,我能最快定位:

```
1. 你执行的命令原文
2. 完整报错信息(别截断)
3. 当前在第几步
```

还原那步如果报错多,直接把 `E:\migration\restore.log` 里过滤后的内容发我。启动失败就发 `app_20260827.log` 里 `APPLICATION FAILED` 往后 20 行。
posted on 2026-08-26 10:45  SheepDog1998  阅读(5)  评论(0)    收藏  举报