平台

从 Postgres 迁移到 Supabase

迁移您现有的 Postgres 数据库到 Supabase。


本指南介绍了将您的 Postgres 数据库迁移到 Supabase 的方法。Supabase 是一个强大且开源的平台。Supabase 提供了开发人员构建产品所需的所有后端功能:Postgres 数据库、身份验证、即时 API、边缘函数、实时订阅和存储。Postgres 是 Supabase 的核心——例如,您可以使用行级别安全性,并且有超过 40 个 Postgres 扩展可用。

本指南演示了如何将您的 Postgres 数据库迁移到 Supabase,以便充分利用 Postgres 的优势,同时获得构建项目所需的所有功能。

本指南提供了三种将您的 Postgres 数据库迁移到 Supabase 的方法

  1. Google Colab - 带有复制粘贴工作流程的引导式笔记本
  2. 手动转储/恢复 - CLI 方法,适用于所有版本
  3. 逻辑复制 - 最小停机时间,需要 Postgres 10+

连接模式#

Supabase 提供了以下连接模式

  • 直接连接
  • Supavisor 会话模式
  • Supavisor 事务模式

对于数据库迁移任务(pg_dump/restore 和逻辑复制),请使用 Supavisor 会话模式。

方法 1:Google Colab(最简单)#

Supabase 提供了一个 Google Colab 迁移笔记本,用于引导式迁移体验:Supabase 迁移 Colab 笔记本

如果您更喜欢一步一步的复制粘贴工作流程,并且设置最少,那么这将是理想的选择。

方法 2:手动转储/恢复#

此方法使用 CLI 工具适用于所有 Postgres 版本。

先决条件#

源 Postgres 要求#

  • 具有运行 pg_dump 权限的连接字符串
  • 转储/恢复不需要特殊设置
  • 来自迁移 VM 的网络访问权限

迁移环境#

  • 运行 Ubuntu 的云 VM,与源数据库或目标数据库位于同一区域
  • 与源数据库版本匹配的 Postgres 客户端工具
  • tmux 用于会话持久性
  • 足够的磁盘空间(通常源数据库大小的 ~50% 就足够了,但具体情况因具体情况而异)

迁移前检查清单#

1
-- Check database size
2
select pg_size_pretty(pg_database_size(current_database())) as size;
3
4
-- Check Postgres version
5
select version();
6
7
-- List installed extensions
8
select * from pg_extension order by extname;
9
10
-- Check active connections
11
select count(*) from pg_stat_activity;

检查 Supabase 中可用的扩展#

1
-- Connect to your Supabase database and check available extensions
2
SELECT name, comment FROM pg_available_extensions ORDER BY name;
3
4
-- Compare with source database extensions
5
SELECT extname FROM pg_extension ORDER BY extname;
6
7
-- Install needed extensions
8
CREATE EXTENSION IF NOT EXISTS extension_name;

步骤 1:设置迁移 VM#

设置 Ubuntu VM#

1
# Install Postgres client and tools
2
sudo apt update
3
sudo apt install software-properties-common
4
sudo sh -c 'echo "deb http://apt.Postgres.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
5
wget --quiet -O - https://www.Postgres.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
6
sudo apt update
7
sudo apt install Postgres-client-17 tmux htop iotop moreutils
8
9
# Start or attach to tmux session
10
tmux a -t migration || tmux new -s migration

步骤 2:准备 Supabase 项目#

  1. supabase.com/dashboard 创建一个 Supabase 项目
  2. 记下您的数据库密码
  3. 通过 SQL 或仪表板安装所需的扩展
  4. 获取您的连接字符串
    • 转到 项目 → 设置 → 数据库 → 连接池
    • 选择 会话池化器(端口 5432)并复制连接字符串
    • 连接格式:Postgres://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:5432/postgres

重要说明:

  • 用户/角色不会迁移 - 您需要在导入后重新创建角色和权限(Supabase 角色指南
  • 表上的行级别安全性 (RLS) 状态不会迁移 - 您需要在迁移后为表启用 RLS。

资源需求:

数据库大小推荐计算推荐 VM所需操作
< 10 GB默认2 vCPU,4 GB RAM无需操作
10-100 GB默认-小4 vCPU,8 GB RAM考虑升级计算
100-500 GB大型计算8 vCPU,16 GB RAM,NVMe在恢复之前升级计算
500 GB - 1 TBXL 计算16 vCPU,32 GB RAM,NVMe在恢复之前升级计算
> 1 TB定制版定制版首先联系支持

此外,如果您希望数据库恢复速度更快,可以通过设置 → 计算和磁盘暂时增加计算大小和/或磁盘 IOPS 和吞吐量(如果您这样做,可以使用更大的 -j 用于 pg_restore)。

步骤 3:创建数据库转储#

对于生产迁移,将源数据库设置为只读模式#

如果在维护窗口期间迁移,请阻止数据更改

1
-- Connect to source database and run:
2
ALTER DATABASE your_database_name SET default_transaction_read_only = true;

对于没有维护窗口的测试,跳过此步骤,但使用较低的 -j 值。

转储数据库#

1
# Determine number of parallel jobs based on:
2
# - Source database CPU cores (don't saturate production)
3
# - VM CPU cores
4
# - For testing without maintenance window: use lower values to be gentle
5
# - For production with maintenance window: can use higher values
6
7
DUMP_JOBS=4 # Adjust based on your setup
8
9
# Check available cores on VM
10
nproc
11
12
# Create dump with progress logging
13
pg_dump \
14
--host=<source_host> \
15
--port=<source_port> \
16
--username=<source_username> \
17
--dbname=<source_database> \
18
--jobs=$DUMP_JOBS \
19
--format=directory \
20
--no-owner \
21
--no-privileges \
22
--no-subscriptions \
23
--verbose \
24
--file=./db_dump 2>&1 | ts | tee -a dump.log

关于转储标志的说明:

  • --no-owner --no-privileges:在转储时应用,以防止 Supabase 用户管理冲突。虽然这些可以在 pg_restore 中使用,但转储期间应用它们可以使转储文件更干净且更可移植。
  • --no-subscriptions:目标中无法使用逻辑复制订阅
  • 转储捕获所有数据和模式,但不排除与 Supabase 托管环境冲突的所有权/权限
  • 要仅迁移单个数据库模式,请将 --schema=PATTERN 参数添加到您的 pg_dump 命令。
  • 要排除一个模式:--exclude-schema=PATTERN
  • 要仅迁移单个表:--table=PATTERN
  • 要排除一个表:--exclude-table=PATTERN

运行 pg_dump --help 以获取完整的选项列表。

数据库大小测试(没有维护窗口)生产(有维护窗口)限制因素
< 10 GB24源 CPU
10-100 GB2-48源 CPU
100-500 GB416磁盘 IOPS
500 GB - 1 TB4-816-32磁盘 IOPS + CPU

注意:对于没有维护窗口的测试,请使用较低的 -j 值以避免影响生产性能。

步骤 4:恢复到 Supabase#

设置连接和恢复#

1
# Set Supabase connection (Session Pooler on port 5432 or direct connection)
2
export SUPABASE_DB_URL="Postgres://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:5432/postgres"
3
4
# Determine restore parallelization based on your Supabase compute size:
5
# Free tier: 2 cores → use -j 2
6
# Small compute: 2 cores → use -j 2
7
# Medium compute: 4 cores → use -j 4
8
# Large compute: 8 cores → use -j 8
9
# XL compute: 16 cores → use -j 16
10
11
RESTORE_JOBS=8 # Adjust based on your Supabase compute size
12
13
# Restore the dump (parallel mode)
14
# Note: -j cannot be used with --single-transaction
15
pg_restore \
16
--dbname="$SUPABASE_DB_URL" \
17
--jobs=$RESTORE_JOBS \
18
--format=directory \
19
--no-owner \
20
--no-privileges \
21
--verbose \
22
./db_dump 2>&1 | ts | tee -a restore.log

如果恢复因扩展错误而失败,请检查错误是否仅与扩展相关。

步骤 5:迁移后任务#

更新统计信息(重要)#

1
psql "$SUPABASE_DB_URL" -c "VACUUM VERBOSE ANALYZE;"

验证迁移#

1
-- Check row counts
2
select schemaname, tablename, n_live_tup
3
from pg_stat_user_tables
4
order by n_live_tup desc
5
limit 20;
6
-- Verify data with application-specific queries

重新启用源上的写入(如果保留)#

1
ALTER DATABASE your_database_name SET default_transaction_read_only = false;

迁移时间估算#

数据库大小转储时间恢复时间总时间
10 GB~5 分钟~10 分钟~15 分钟
100 GB~30 分钟~45 分钟~1.5 小时
500 GB~2 小时~3 小时~5 小时
1 TB~4 小时~6 小时~10 小时

时间因硬件、网络和并行化设置而异

重要说明#

  1. 区域邻近很重要:VM 应与源或目标位于同一区域,以获得最佳性能
  2. 降级迁移:虽然在某些情况下在技术上是可行的,但强烈不建议
  3. 没有停机时间的测试:使用较低的 -j 值进行 pg_dump 以避免影响生产
  4. 对于 pg_restore:可以使用完全并行化,而不会影响生产
  5. 监控资源:使用 htopiotop 监控 CPU、磁盘 I/O
  6. 磁盘 I/O:通常在网络带宽之前成为瓶颈

方法 3:逻辑复制#

此方法允许使用 Postgres 的逻辑复制功能进行最小停机时间的迁移。源数据库和目标数据库都需要 Postgres 10+。

何时使用逻辑复制#

  • 您需要最小的停机时间(几分钟而不是几小时)
  • 源数据库是 Postgres 10 或更高版本
  • 您可以在源上配置逻辑复制
  • 数据库具有高写入活动,无法长时间暂停

源 Postgres 先决条件#

访问权限和权限#

  • 具有创建发布和读取表的权限的连接字符串
  • 推荐超级用户或复制权限

逻辑复制所需的设置#

  • wal_level = logical
  • max_wal_senders ≥ 1
  • max_replication_slots ≥ 1
  • 足够的 max_connections(当前 + 1 用于订阅)

复制标识#

每个接收 UPDATE/DELETE 的表都必须具有复制标识(通常是 PRIMARY KEY)。对于没有一个的表

1
ALTER TABLE schema.table_name REPLICA IDENTITY FULL;

未复制的项目#

  • DDL 更改(模式修改)
  • 序列(需要手动同步)
  • 大型对象 (LOB)(使用转储/恢复或存储在常规 bytea 列中)

计划模式冻结、序列同步,并在切换之前单独处理 LOB。

步骤 1:配置源数据库#

编辑 Postgres 配置文件

Postgres.conf#

1
# Set Supabase connection (Session Pooler on port 5432 or direct connection)
2
export SUPABASE_DB_URL="Postgres://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:5432/postgres"
3
4
# Set WAL level to logical
5
wal_level = logical
6
7
# Ensure sufficient replication slots
8
max_replication_slots = 10
9
10
# Ensure sufficient WAL senders
11
max_wal_senders = 10
12
13
# Set appropriate max_connections (current connections + 1 for subscription)
14
max_connections = 200 # Adjust based on your needs
15
16
# Optional: Enable SSL for secure replication
17
ssl = on
18
19
# Allow connections from Supabase
20
listen_addresses = '*' # Or specific IP addresses

pg_hba.conf#

1
# Allow replication connections from Supabase
2
# Replace <supabase_ip_range> with actual Supabase IP range
3
host replication all <supabase_ip_range> md5
4
host all all <supabase_ip_range> md5
5
6
# With SSL:
7
hostssl replication all <supabase_ip_range> md5
8
hostssl all all <supabase_ip_range> md5

重新启动 Postgres

1
sudo systemctl restart Postgres
2
sudo systemctl status Postgres

步骤 2:验证配置#

1
-- Should return 'logical'
2
SHOW wal_level;
3
4
-- Check other parameters
5
SHOW max_replication_slots;
6
SHOW max_wal_senders;
7
8
-- Check current connections
9
SELECT count(*) FROM pg_stat_activity;

步骤 3:检查并设置复制标识#

1
-- Find tables without primary keys
2
SELECT n.nspname, c.relname
3
FROM pg_class c
4
JOIN pg_namespace n ON n.oid = c.relnamespace
5
LEFT JOIN pg_constraint pk ON pk.conrelid = c.oid AND pk.contype = 'p'
6
WHERE c.relkind = 'r'
7
AND pk.oid IS NULL
8
AND n.nspname NOT IN ('pg_catalog','information_schema');
9
10
-- For tables without a primary key, set REPLICA IDENTITY FULL
11
ALTER TABLE my_schema.my_table REPLICA IDENTITY FULL;

步骤 4:仅导出和恢复模式#

1
# Export schema from source
2
pg_dump \
3
-h <source_host> \
4
-U <source_user> \
5
-p <source_port> \
6
-d <source_database> \
7
--schema-only \
8
--no-privileges \
9
--no-subscriptions \
10
--format=directory \
11
-f ./schema_dump
12
13
# Restore schema to Supabase (use Session Pooler)
14
pg_restore \
15
--dbname="$SUPABASE_DB_URL" \
16
--format=directory \
17
--schema-only \
18
--no-privileges \
19
--single-transaction \
20
--verbose \
21
./schema_dump

步骤 5:在源上创建发布#

1
-- Create publication for all tables
2
CREATE PUBLICATION supabase_migration FOR ALL TABLES;
3
4
-- Or for specific tables only (doesn't require superuser)
5
CREATE PUBLICATION supabase_migration FOR TABLE
6
schema1.table1,
7
schema1.table2,
8
public.table3;
9
10
-- Verify publication was created
11
SELECT * FROM pg_publication;

步骤 6:在 Supabase 上创建订阅#

连接到您的 Supabase 数据库

1
-- Create subscription with SSL (recommended)
2
CREATE SUBSCRIPTION supabase_subscription
3
CONNECTION 'host=<source_host> port=<source_port> user=<source_user> password=<source_password> dbname=<source_database> sslmode=require'
4
PUBLICATION supabase_migration;
5
6
-- Or without SSL (if source doesn't support it)
7
CREATE SUBSCRIPTION supabase_subscription
8
CONNECTION 'host=<source_host> port=<source_port> user=<source_user> password=<source_password> dbname=<source_database> sslmode=disable'
9
PUBLICATION supabase_migration;

步骤 7:监控复制状态#

1
-- On Supabase (subscriber) - check subscription status
2
select * from pg_subscription_rel;
3
4
-- srsubstate = 'r' means ready (synchronized)
5
-- srsubstate = 'i' means initializing
6
-- srsubstate = 'd' means data is being copied
7
8
-- Overall subscription status
9
select * from pg_stat_subscription;
10
11
-- On source database - check replication status
12
select * from pg_stat_replication;
13
14
-- Check replication lag
15
select
16
slot_name,
17
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) as lag_size
18
from pg_replication_slots;

等待直到所有表都显示 srsubstate = 'r'(就绪)状态。

步骤 8:同步序列#

在初始数据同步完成后,但在切换到 Supabase 之前

1
# Set source to read-only
2
psql -h <source_host> -c "ALTER DATABASE <source_database> SET default_transaction_read_only = true;"
3
4
# Export sequences from source
5
pg_dump \
6
-h <source_host> \
7
-U <source_user> \
8
-p <source_port> \
9
-d <source_database> \
10
--data-only \
11
--table='*_seq' \
12
--table='*_id_seq' > sequences.sql
13
14
# Import sequences to Supabase
15
psql "$SUPABASE_DB_URL" -f sequences.sql

第 9 步:切换到 Supabase#

  1. 确保复制滞后为零
1
-- On Supabase
2
select * from pg_stat_subscription;
3
-- Check that latest_end_lsn is current
  1. 停止对源数据库的写入(如果尚未设置为只读)

  2. 删除 Supabase 上的订阅

1
DROP SUBSCRIPTION supabase_subscription;
  1. 更新应用程序连接字符串以指向 Supabase

  2. 验证应用程序功能

第 10 步:清理#

在源数据库上(成功迁移后)

1
-- Remove publication
2
DROP PUBLICATION supabase_migration;
3
4
-- Check and remove any remaining replication slots
5
SELECT * FROM pg_replication_slots;
6
DROP REPLICATION SLOT slot_name; -- if any remain
7
8
-- The source database should remain read-only or be decommissioned
9
-- Do NOT re-enable writes to avoid a split-brain scenario!

故障排除逻辑复制#

问题解决方案
"无法连接到发布者"检查网络连接、防火墙规则、pg_hba.conf
"角色不存在"确保复制用户存在于源数据库上,并具有 REPLICATION 权限
"发布不存在"验证发布名称以及是否已成功创建
复制滞后增加检查网络带宽、源数据库负载,添加更多 WAL 发送者
表卡在 i 状态检查源表上的锁,验证表结构是否匹配
"复制槽用完"在 Postgres.conf 中增加 max_replication_slots

重要限制#

  • DDL 更改:架构修改不会被复制 - 在迁移期间冻结架构
  • 序列:在切换之前需要手动同步
  • 大对象 (LOB):不会被复制 - 使用转储/恢复或存储在常规 bytea 列中
  • 自定义类型:可能需要特殊处理
  • 用户和角色:必须在 Supabase 上手动重新创建

有关详细限制,请参阅 Postgres 逻辑复制限制

何时使用哪种方法#

使用转储/恢复时

  • 可以接受停机窗口
  • 源数据库是 Postgres < 10
  • 首选更简单的流程
  • 无法在源数据库上配置逻辑复制

使用逻辑复制时

  • 需要最少的停机时间
  • 双方都是 Postgres 10+
  • 可以修改源配置
  • 具有复制权限

获取帮助#