---
title: "把 Zeabur 上的 PostgreSQL 搬到 Zenavo：恢复备份并核对数据"
description: "将 Zeabur 导出的 PostgreSQL 备份直接上传恢复到 Zenavo，了解版本、角色权限与 pg_dumpall 集群备份的处理规则，并完成数据核对与切流。"
date: 2026-11-10
language: zh-CN
url: https://zenavo.app/zh-CN/blog/migrate-zeabur-postgresql
---

# 把 Zeabur 上的 PostgreSQL 搬到 Zenavo：恢复备份并核对数据

从 Zeabur 搬家时，应用服务可以通过导入模板一键建好，而数据库里的数据需要通过备份搬过来。

好消息是：Zenavo 原生支持 Zeabur 下载的 PostgreSQL 备份包，不用在本地解压、也不用自己改写 SQL。这篇文章专门讲怎么把 Zeabur 的 PostgreSQL 备份恢复到 Zenavo、核对数据并切换应用。整个项目的搬迁流程可以配合[Zeabur 迁移完整指南](https://zenavo.app/zh-CN/blog/migrate-from-zeabur)一起看。

## 1. 从 Zeabur 下载备份

在 Zeabur 的 PostgreSQL 服务里下载备份，同时记下两件事：

- **PostgreSQL 的主版本号**（例如 15、16 或 17）；
- **应用实际使用的数据库名称**。

Zeabur 下载下来的文件通常是一个 `.zip` 包（有时文件名里带着 `.sql.gz`，但外层其实是 ZIP），里面是一份 `pg_dumpall` 导出的集群 SQL。**保持下载下来的原始文件不动即可**，Zenavo 支持直接上传 Zeabur 的 `.zip` 备份包，也支持普通的 `.sql` 和 `.sql.gz`。

## 2. 在 Zenavo 创建同版本的空数据库

在 Zenavo 的目标项目里点击 **添加服务 → 数据库 → PostgreSQL**，选择**与 Zeabur 上相同的主版本**并部署。

注意三点：

- **主版本一致最稳妥**：Zenavo 会阻止把高版本（如 PG 18）的集群备份恢复到低版本（如 PG 17）；先按同版本搬过来，以后有需要再单独升级。
- **保持目标库为空**：不要先启动会自动建表的应用服务，再去恢复备份。如果应用已经建了表，集群备份恢复会因为目标库非空而报错。
- **检查扩展**：如果你的库用了 PostGIS、`pgvector` 等扩展，确认目标镜像包含对应扩展。

## 3. 上传备份并恢复

打开新建的 PostgreSQL 服务的 **备份与恢复（Backup & Restore）** 页，点击上传，选中第 1 步从 Zeabur 下载的备份文件，然后执行恢复。

因为 Zeabur 的备份是 `pg_dumpall` 集群导出，Zenavo 在恢复时会自动做以下转换（详见[备份与恢复文档](https://docs.zenavo.app/zh-CN/operations/backups#uploading-a-backup)）：

- 将里面的应用数据库内容恢复到当前服务的数据库；
- 自动创建备份里用到的业务角色，并保留其角色密码和行级安全策略（RLS）；
- 出于安全考虑，把 Zeabur 原有的 `root` 超级用户降权为**无超级权限、禁止直接登录**的角色，同时保留它的表和对象所有权；
- 自动跳过系统库 `template1` 和 `postgres`。

> 如果一份集群备份里包含多个非系统的业务数据库，恢复会报错提示先拆分；绝大多数 Zeabur 项目只有一个业务数据库，直接上传即可。

如果恢复中途因为扩展缺失等原因报错，修好原因后建议删掉这个半成品数据库服务、重新建一个干净的 PostgreSQL 再恢复，不要在只恢复了一半的库上反复重试。

## 4. 核对表结构和行数

恢复完成后，点开 PostgreSQL 服务的 **[数据库视图（Database View）](https://docs.zenavo.app/zh-CN/deploy/databases#database-view)**，可以直接在网页上跑只读 SQL 核对数据。

先看所有的业务表是不是都在：

```sql
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_type = 'BASE TABLE'
  AND table_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY table_schema, table_name;
```

再挑几张核心业务表（比如用户表、订单表），跟 Zeabur 上的数据对比准确行数和最新一条记录的时间：

```sql
SELECT count(*) AS row_count, max(created_at) AS latest_created_at
FROM public.orders;
```

（注意不要只看系统统计视图里的估算行数 `reltuples`，用 `count(*)` 核对更准。）

## 5. 连接应用并完成切换

在同项目的应用服务里，把数据库连接变量指向新的 PostgreSQL 服务（数据库服务名为 `postgresql` 时）：

```dotenv
DATABASE_URL=${POSTGRESQL_CONNECTION_STRING}
```

注意：不要继续硬编码 Zeabur 旧的 `root` 账号去连接，直接用 Zenavo 新数据库暴露出来的连接变量（考虑到 Zeabur 之前的安全事件，换用新数据库自动生成的新密码也更安全）。

如果你希望切换期间**零数据丢失**，正式切流时按这个顺序操作：

1. 先用一份当前备份在 Zenavo 上演练一遍恢复，确认应用连上后登录、查询、新建记录都正常；
2. 挑选低峰期，先把 Zeabur 上的应用暂停（或停掉定时任务、队列消费者，避免再有新数据写入旧库）；
3. 在 Zeabur 上打最后一份最新备份，上传到 Zenavo 恢复到一个干净的 PostgreSQL 服务中；
4. 启动 Zenavo 上的应用并切换域名 DNS。

## 常见问题

**恢复时报错提示目标数据库不为空（database is not empty）？**
说明你的应用服务在恢复前已经连上数据库并自动执行了建表（如 Prisma / TypeORM 自动迁移）。先把应用停掉，删掉并重建一个新的 PostgreSQL 服务，**先恢复备份、再启动应用**。

**恢复成功后，应用报错 `permission denied` 或登录失败？**
Zeabur 备份里的旧 `root` 超级用户会被设为禁止登录。把应用的 `DATABASE_URL` 改成引用 Zenavo 数据库服务的 `${POSTGRESQL_CONNECTION_STRING}`（使用默认的 `postgres` 账号和新密码）即可。

**Zeabur 下载下来的文件后缀是 `.sql.gz`，打不开也解压不了？**
这是 Zeabur 导出时的命名问题——它实际上是一个 `.zip` 压缩包。不用改名也不用自己解压，直接原样上传到 Zenavo 的 **备份与恢复** 就能识别。

数据库恢复核对无误后，记得在 Zenavo 的 **备份与恢复** 里顺手开启定期自动备份。完整搬家步骤见[Zeabur 迁移指南](https://zenavo.app/zh-CN/blog/migrate-from-zeabur)。
