从 Postgres 迁移到 Supabase
迁移您现有的 Postgres 数据库到 Supabase。
本指南介绍了将您的 Postgres 数据库迁移到 Supabase 的方法。Supabase 是一个强大且开源的平台。Supabase 提供了开发人员构建产品所需的所有后端功能:Postgres 数据库、身份验证、即时 API、边缘函数、实时订阅和存储。Postgres 是 Supabase 的核心——例如,您可以使用行级别安全性,并且有超过 40 个 Postgres 扩展可用。
本指南演示了如何将您的 Postgres 数据库迁移到 Supabase,以便充分利用 Postgres 的优势,同时获得构建项目所需的所有功能。
本指南提供了三种将您的 Postgres 数据库迁移到 Supabase 的方法
- Google Colab - 带有复制粘贴工作流程的引导式笔记本
- 手动转储/恢复 - CLI 方法,适用于所有版本
- 逻辑复制 - 最小停机时间,需要 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 size2select pg_size_pretty(pg_database_size(current_database())) as size;34-- Check Postgres version5select version();67-- List installed extensions8select * from pg_extension order by extname;910-- Check active connections11select count(*) from pg_stat_activity;检查 Supabase 中可用的扩展#
1-- Connect to your Supabase database and check available extensions2SELECT name, comment FROM pg_available_extensions ORDER BY name;34-- Compare with source database extensions5SELECT extname FROM pg_extension ORDER BY extname;67-- Install needed extensions8CREATE EXTENSION IF NOT EXISTS extension_name;步骤 1:设置迁移 VM#
为了获得最佳性能,请从云 VM 而不是您的本地机器运行迁移。VM 应与您的源数据库或目标数据库位于同一区域,以优化网络性能。请参阅步骤 2 中的资源需求表,了解 VM 大小的建议。
设置 Ubuntu VM#
1# Install Postgres client and tools2sudo apt update3sudo apt install software-properties-common4sudo sh -c 'echo "deb http://apt.Postgres.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'5wget --quiet -O - https://www.Postgres.org/media/keys/ACCC4CF8.asc | sudo apt-key add -6sudo apt update7sudo apt install Postgres-client-17 tmux htop iotop moreutils89# Start or attach to tmux session10tmux a -t migration || tmux new -s migration步骤 2:准备 Supabase 项目#
- 在 supabase.com/dashboard 创建一个 Supabase 项目
- 记下您的数据库密码
- 通过 SQL 或仪表板安装所需的扩展
- 获取您的连接字符串
- 转到 项目 → 设置 → 数据库 → 连接池
- 选择 会话池化器(端口 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 TB | XL 计算 | 16 vCPU,32 GB RAM,NVMe | 在恢复之前升级计算 |
| > 1 TB | 定制版 | 定制版 | 首先联系支持 |
此外,如果您希望数据库恢复速度更快,可以通过设置 → 计算和磁盘暂时增加计算大小和/或磁盘 IOPS 和吞吐量(如果您这样做,可以使用更大的 -j 用于 pg_restore)。
步骤 3:创建数据库转储#
对于生产迁移,将源数据库设置为只读模式#
如果在维护窗口期间迁移,请阻止数据更改
1-- Connect to source database and run:2ALTER 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 cores4# - For testing without maintenance window: use lower values to be gentle5# - For production with maintenance window: can use higher values67DUMP_JOBS=4 # Adjust based on your setup89# Check available cores on VM10nproc1112# Create dump with progress logging13pg_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 以获取完整的选项列表。
推荐并行化 (-j 值)#
| 数据库大小 | 测试(没有维护窗口) | 生产(有维护窗口) | 限制因素 |
|---|---|---|---|
| < 10 GB | 2 | 4 | 源 CPU |
| 10-100 GB | 2-4 | 8 | 源 CPU |
| 100-500 GB | 4 | 16 | 磁盘 IOPS |
| 500 GB - 1 TB | 4-8 | 16-32 | 磁盘 IOPS + CPU |
注意:对于没有维护窗口的测试,请使用较低的 -j 值以避免影响生产性能。
步骤 4:恢复到 Supabase#
设置连接和恢复#
1# Set Supabase connection (Session Pooler on port 5432 or direct connection)2export SUPABASE_DB_URL="Postgres://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:5432/postgres"34# Determine restore parallelization based on your Supabase compute size:5# Free tier: 2 cores → use -j 26# Small compute: 2 cores → use -j 27# Medium compute: 4 cores → use -j 48# Large compute: 8 cores → use -j 89# XL compute: 16 cores → use -j 161011RESTORE_JOBS=8 # Adjust based on your Supabase compute size1213# Restore the dump (parallel mode)14# Note: -j cannot be used with --single-transaction15pg_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:迁移后任务#
更新统计信息(重要)#
1psql "$SUPABASE_DB_URL" -c "VACUUM VERBOSE ANALYZE;"对于 Postgres 18+,pg_dump 包含带有 --with-statistics 的统计信息,但您仍然应该运行 VACUUM 以获得最佳性能。
验证迁移#
1-- Check row counts2select schemaname, tablename, n_live_tup3from pg_stat_user_tables4order by n_live_tup desc5limit 20;6-- Verify data with application-specific queries重新启用源上的写入(如果保留)#
1ALTER 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 小时 |
时间因硬件、网络和并行化设置而异
重要说明#
- 区域邻近很重要:VM 应与源或目标位于同一区域,以获得最佳性能
- 降级迁移:虽然在某些情况下在技术上是可行的,但强烈不建议
- 没有停机时间的测试:使用较低的
-j值进行 pg_dump 以避免影响生产 - 对于 pg_restore:可以使用完全并行化,而不会影响生产
- 监控资源:使用
htop、iotop监控 CPU、磁盘 I/O - 磁盘 I/O:通常在网络带宽之前成为瓶颈
方法 3:逻辑复制#
此方法允许使用 Postgres 的逻辑复制功能进行最小停机时间的迁移。源数据库和目标数据库都需要 Postgres 10+。
何时使用逻辑复制#
- 您需要最小的停机时间(几分钟而不是几小时)
- 源数据库是 Postgres 10 或更高版本
- 您可以在源上配置逻辑复制
- 数据库具有高写入活动,无法长时间暂停
源 Postgres 先决条件#
访问权限和权限#
- 具有创建发布和读取表的权限的连接字符串
- 推荐超级用户或复制权限
逻辑复制所需的设置#
wal_level = logicalmax_wal_senders ≥ 1max_replication_slots ≥ 1- 足够的
max_connections(当前 + 1 用于订阅)
复制标识#
每个接收 UPDATE/DELETE 的表都必须具有复制标识(通常是 PRIMARY KEY)。对于没有一个的表
1ALTER 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)2export SUPABASE_DB_URL="Postgres://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:5432/postgres"34# Set WAL level to logical5wal_level = logical67# Ensure sufficient replication slots8max_replication_slots = 10910# Ensure sufficient WAL senders11max_wal_senders = 101213# Set appropriate max_connections (current connections + 1 for subscription)14max_connections = 200 # Adjust based on your needs1516# Optional: Enable SSL for secure replication17ssl = on1819# Allow connections from Supabase20listen_addresses = '*' # Or specific IP addressespg_hba.conf#
1# Allow replication connections from Supabase2# Replace <supabase_ip_range> with actual Supabase IP range3host replication all <supabase_ip_range> md54host all all <supabase_ip_range> md556# With SSL:7hostssl replication all <supabase_ip_range> md58hostssl all all <supabase_ip_range> md5重新启动 Postgres
1sudo systemctl restart Postgres2sudo systemctl status Postgres步骤 2:验证配置#
1-- Should return 'logical'2SHOW wal_level;34-- Check other parameters5SHOW max_replication_slots;6SHOW max_wal_senders;78-- Check current connections9SELECT count(*) FROM pg_stat_activity;步骤 3:检查并设置复制标识#
1-- Find tables without primary keys2SELECT n.nspname, c.relname3FROM pg_class c4JOIN pg_namespace n ON n.oid = c.relnamespace5LEFT JOIN pg_constraint pk ON pk.conrelid = c.oid AND pk.contype = 'p'6WHERE c.relkind = 'r'7 AND pk.oid IS NULL8 AND n.nspname NOT IN ('pg_catalog','information_schema');910-- For tables without a primary key, set REPLICA IDENTITY FULL11ALTER TABLE my_schema.my_table REPLICA IDENTITY FULL;步骤 4:仅导出和恢复模式#
1# Export schema from source2pg_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_dump1213# Restore schema to Supabase (use Session Pooler)14pg_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 tables2CREATE PUBLICATION supabase_migration FOR ALL TABLES;34-- Or for specific tables only (doesn't require superuser)5CREATE PUBLICATION supabase_migration FOR TABLE6 schema1.table1,7 schema1.table2,8 public.table3;910-- Verify publication was created11SELECT * FROM pg_publication;步骤 6:在 Supabase 上创建订阅#
连接到您的 Supabase 数据库
1-- Create subscription with SSL (recommended)2CREATE SUBSCRIPTION supabase_subscription3CONNECTION 'host=<source_host> port=<source_port> user=<source_user> password=<source_password> dbname=<source_database> sslmode=require'4PUBLICATION supabase_migration;56-- Or without SSL (if source doesn't support it)7CREATE SUBSCRIPTION supabase_subscription8CONNECTION 'host=<source_host> port=<source_port> user=<source_user> password=<source_password> dbname=<source_database> sslmode=disable'9PUBLICATION supabase_migration;步骤 7:监控复制状态#
1-- On Supabase (subscriber) - check subscription status2select * from pg_subscription_rel;34-- srsubstate = 'r' means ready (synchronized)5-- srsubstate = 'i' means initializing6-- srsubstate = 'd' means data is being copied78-- Overall subscription status9select * from pg_stat_subscription;1011-- On source database - check replication status12select * from pg_stat_replication;1314-- Check replication lag15select16 slot_name,17 pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) as lag_size18from pg_replication_slots;等待直到所有表都显示 srsubstate = 'r'(就绪)状态。
步骤 8:同步序列#
在初始数据同步完成后,但在切换到 Supabase 之前
1# Set source to read-only2psql -h <source_host> -c "ALTER DATABASE <source_database> SET default_transaction_read_only = true;"34# Export sequences from source5pg_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.sql1314# Import sequences to Supabase15psql "$SUPABASE_DB_URL" -f sequences.sql第 9 步:切换到 Supabase#
- 确保复制滞后为零
1-- On Supabase2select * from pg_stat_subscription;3-- Check that latest_end_lsn is current-
停止对源数据库的写入(如果尚未设置为只读)
-
删除 Supabase 上的订阅
1DROP SUBSCRIPTION supabase_subscription;-
更新应用程序连接字符串以指向 Supabase
-
验证应用程序功能
第 10 步:清理#
在源数据库上(成功迁移后)
1-- Remove publication2DROP PUBLICATION supabase_migration;34-- Check and remove any remaining replication slots5SELECT * FROM pg_replication_slots;6DROP REPLICATION SLOT slot_name; -- if any remain78-- The source database should remain read-only or be decommissioned9-- 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+
- 可以修改源配置
- 具有复制权限
获取帮助#
- 对于大于 150 GB 的数据库:联系 Supabase 支持 团队再开始
- Supabase 控制面板支持
- Supabase Discord
- Postgres 角色和权限指南
- 行级别安全指南