从 Postgres 迁移到 Supabase
Migrate your existing Postgres database to Supabase.
这是一个将你的 Postgres 数据库迁移到 Supabase 的指南。Supabase 是一个强大且开源的平台。Supabase 提供开发者构建产品所需的所有后端功能:Postgres 数据库、身份验证、即时 API、边缘函数、实时订阅和存储。Postgres 是 Supabase 的核心——例如,你可以使用行级安全,而且还有超过 40 个可用的 Postgres 扩展。
🌐 This is a guide for migrating your Postgres database to Supabase. Supabase is a robust and open-source platform. Supabase provides all the backend features developers need to build a product: a Postgres database, authentication, instant APIs, edge functions, real-time subscriptions, and storage. Postgres is the core of Supabase—for example, you can use row-level security, and there are more than 40 Postgres extensions available.
本指南演示了如何将你的 Postgres 数据库迁移到 Supabase,让你在充分利用 Postgres 的同时,还能获得构建项目所需的所有功能。
🌐 This guide demonstrates how to migrate your Postgres database to Supabase to get the most out of Postgres while gaining access to all the features you need to build a project.
本指南提供三种将你的 Postgres 数据库迁移到 Supabase 的方法:
🌐 This guide provides three methods for migrating your Postgres database to Supabase:
- Google Colab - 带有复制粘贴工作流程的指导注意本
- 手动导出/恢复 - 命令行方式,适用于所有版本
- 逻辑复制 - 最小停机时间,需要 Postgres 10+
连接模式 #
🌐 Connection modes
Supabase 提供以下连接模式:
🌐 Supabase provides the following connection modes:
- 直接连接
- Supavisor 会话模式
- Supavisor 交易模式
在数据库迁移任务(pg_dump/恢复和逻辑复制)中使用 Supavisor 会话模式。
🌐 Use Supavisor session mode for the database migration tasks (pg_dump/restore and logical replication).
方法1:谷歌Colab(最简单) #
🌐 Method 1: Google Colab (easiest)
Supabase 提供了一个 Google Colab 迁移注意本,让迁移过程更有指导性: Supabase 迁移 Colab 注意本
🌐 Supabase provides a Google Colab migration notebook for a guided migration experience: Supabase Migration Colab Notebook
如果你喜欢逐步、复制粘贴的工作流程,而且设置很少的话,这就很理想。
🌐 This is ideal if you prefer a step-by-step, copy-paste workflow with minimal setup.
方法二:手动导出/恢复 #
🌐 Method 2: Manual dump/restore
这种方法适用于所有使用命令行工具的 Postgres 版本。
🌐 This method works for all Postgres versions using CLI tools.
先决条件 #
🌐 Prerequisites
源 Postgres 要求 #
🌐 Source Postgres requirements
- 拥有运行
pg_dump权限的连接字符串 - 转储/恢复不需要特殊设置
- 来自迁移虚拟机的网络访问
迁移环境 #
🌐 Migration environment
- 与源数据库或目标数据库在同一地区运行的 Ubuntu 云虚拟机
- 与你的源数据库版本匹配的 Postgres 客户端工具
- tmux 用于会话持久化
- 足够的磁盘空间(通常大约是源数据库大小的50%就够了,但具体情况会有所不同)
迁移前清单 #
🌐 Pre-Migration checklist
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 中可用的扩展 #
🌐 Check available extensions in 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步:设置迁移虚拟机 #
🌐 Step 1: Set up migration VM
为了获得最佳性能,请从云虚拟机运行迁移,而不是从本地电脑。虚拟机应位于源数据库或目标数据库所在的同一地区,以优化网络性能。有关虚拟机大小的建议,请参阅第2步的资源要求表。
🌐 For optimal performance, run the migration from a cloud VM, not your local machine. The VM should be in the same region as either your source or target database to optimize network performance. See the Resource Requirements table in Step 2 for VM sizing recommendations.
设置 Ubuntu 虚拟机 #
🌐 Set up 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 项目 #
🌐 Step 2: Prepare Supabase project
- 在 supabase.com/dashboard 创建一个 Supabase 项目
- 记下你的数据库密码
- 通过 SQL 或仪表板安装所需的扩展
- 获取你的连接字符串:
- 去 项目 → 设置 → 数据库 → 连接池
- 选择 会话池(端口 5432)并复制连接字符串
- 连接格式:
Postgres://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:5432/postgres
重要注意事项:
- 用户/角色不会被迁移 - 导入后你需要重新创建角色和权限 (Supabase 角色指南)
- 表的行级安全性 (RLS) 状态未迁移 - 迁移后你需要为表启用 RLS。
资源需求:
| 数据库大小 | 推荐计算资源 | 推荐虚拟机 | 需要的操作 |
|---|---|---|---|
| < 10 GB | Default | 2 vCPUs, 4 GB RAM | None |
| 10-100 GB | Default-Small | 4 vCPUs, 8 GB RAM | Consider compute upgrade |
| 100-500 GB | Large compute | 8 vCPUs, 16 GB RAM, NVMe | Upgrade compute before restore |
| 500 GB - 1 TB | XL compute | 16 vCPUs, 32 GB RAM, NVMe | Upgrade compute before restore |
| > 1 TB | 自定义 | 自定义 | 先联系支持 |
另外,如果你想要更快地恢复数据库,你可以通过 设置 → 计算和磁盘 临时增加计算资源和/或磁盘 IOPS 及吞吐量(如果这样做,你可以在 pg_restore 中使用更大的 -j)。
🌐 Also, you can temporarily increase compute size and/or disk IOPS and throughput via Settings → Compute and Disk if you want faster database restore (you can use larger -j for pg_restore if you do so).
步骤 3:创建数据库转储 #
🌐 Step 3: Create database dump
将源数据库设置为只读模式以进行生产迁移 #
🌐 Set source database to read only mode for production migration
如果在进行维护窗口迁移,请防止数据更改:
🌐 If doing a maintenance window migration, prevent data changes:
1-- Connect to source database and run:2ALTER DATABASE your_database_name SET default_transaction_read_only = true;如果没有维护时间进行测试,可以跳过这一步,但使用较低的 -j 值。
🌐 For testing without a maintenance window, skip this step but use lower -j values.
导出数据库 #
🌐 Dump the database
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 管理环境冲突的所有权/权限
- 如果只想迁移一个数据库模式,在你的
pg_dump命令中添加--schema=PATTERN参数。 - 要排除一个模式:
--exclude-schema=PATTERN。 - 只迁移一个表:
--table=PATTERN。 - 要排除一个表:
--exclude-table=PATTERN。
运行 pg_dump --help 查看完整选项列表。
🌐 Run pg_dump --help for a full list of options.
推荐的并行化(-j 数值) #
🌐 Recommended parallelization (-j values)
| 数据库大小 | 测试环境(无维护窗口) | 生产环境(有维护窗口) | 限制因素 |
|---|---|---|---|
| < 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 #
🌐 Step 4: Restore to Supabase
设置连接并恢复 #
🌐 Set connection and restore
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如果恢复失败并出现扩展错误,检查错误是否仅与扩展相关。
🌐 If restore fails with extension errors, check that errors are only extension-related.
第5步:迁移后的任务 #
🌐 Step 5: Post-Migration tasks
更新统计数据(重要) #
🌐 Update statistics (important)
1psql "$SUPABASE_DB_URL" -c "VACUUM VERBOSE ANALYZE;"对于 Postgres 18 及以上版本,pg_dump 会包含带有 --with-statistics 的统计信息,但你仍然应该运行 VACUUM 以获得最佳性能。
🌐 For Postgres 18+, pg_dump includes statistics with --with-statistics, but you should still run VACUUM for optimal performance.
验证迁移 #
🌐 Verify migration
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重新启用源上的写入(如果要保留的话) #
🌐 Re-enable writes on source (if keeping it)
1ALTER DATABASE your_database_name SET default_transaction_read_only = false;迁移时间估计 #
🌐 Migration time estimates
| 数据库大小 | 导出时间 | 恢复时间 | 总时间 |
|---|---|---|---|
| 10 GB | 约5分钟 | 约10分钟 | 约15分钟 |
| 100 GB | 约30分钟 | 约45分钟 | 约1.5小时 |
| 500 GB | 约2小时 | 约3小时 | 约5小时 |
| 1 TB | 约4小时 | 约6小时 | 约10小时 |
时间会根据硬件、网络和并行设置而变化
🌐 Times vary based on hardware, network, and parallelization settings
重要提示 #
🌐 Important notes
- 区域接近很重要:虚拟机应与源或目标位于同一区域,以获得最佳性能
- 降级迁移:虽然在某些情况下技术上可行,但强烈不推荐
- 无停机测试:使用较低的
-j值进行 pg_dump,以避免影响生产环境 - 对于 pg_restore:可以使用完全并行化,不管对生产的影响
- 监控资源:使用
htop、iotop观察 CPU、磁盘 I/O - 磁盘 I/O:通常是在网络带宽之前的瓶颈
方法3:逻辑复制 #
🌐 Method 3: Logical replication
这种方法利用 Postgres 的逻辑复制功能实现最小停机时间的迁移。要求源和目标都使用 Postgres 10 及以上版本。
🌐 This method allows migration with minimal downtime using Postgres's logical replication feature. Requires Postgres 10+ on both source and target.
什么时候使用逻辑复制 #
🌐 When to use logical replication
- 你只需要很短的停机时间(几分钟而不是几小时)
- 源数据库是 Postgres 10 或更高版本
- 你可以在源端配置逻辑复制
- 数据库写入活动很频繁,不能长时间暂停
源 Postgres 前提条件 #
🌐 Source Postgres prerequisites
访问权限 #
🌐 Access & privileges
- 具有创建发布权限并能读取表的连接字符串
- 建议使用超级用户或复制权限
逻辑复制所需设置 #
🌐 Required settings for logical replication
wal_level = logicalmax_wal_senders ≥ 1max_replication_slots ≥ 1- 足够的
max_connections(当前 + 1 用于订阅)
复制身份 #
🌐 Replica identity
每个接收 UPDATE/DELETE 的表必须有一个副本标识(通常是主键)。对于没有的表:
🌐 Every table receiving UPDATE/DELETE must have a replica identity (typically a PRIMARY KEY). For tables without one:
1ALTER TABLE schema.table_name REPLICA IDENTITY FULL;未复制的物品 #
🌐 Non-Replicated items
- DDL 更改(模式修改)
- 序列(需要手动同步)
- 大对象 (LOBs)(使用 dump/restore 或存储在常规 bytea 列中)
计划一下模式冻结,切换前同步序列,并单独处理大对象。
🌐 Plan a schema freeze, sequence sync before cutover, and handle LOBs separately.
步骤 1:配置源数据库 #
🌐 Step 1: Configure source database
编辑 Postgres 配置文件:
🌐 Edit Postgres configuration files:
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:
🌐 Restart Postgres:
1sudo systemctl restart Postgres2sudo systemctl status Postgres步骤 2:核实配置 #
🌐 Step 2: Verify configuration
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:检查并设置副本标识 #
🌐 Step 3: Check and set replica identity
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:仅导出并恢复模式 #
🌐 Step 4: Export and restore schema only
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步:在源上创建发布物 #
🌐 Step 5: Create publication on source
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上创建订阅 #
🌐 Step 6: Create subscription on Supabase
连接到你的 Supabase 数据库:
🌐 Connect to your Supabase database:
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:监控复制状态 #
🌐 Step 7: Monitor replication status
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'(准备好)状态。
🌐 Wait until all tables show srsubstate = 'r' (ready) status.
步骤 8:同步序列 #
🌐 Step 8: Synchronize sequences
在初始数据同步完成之后,但在切换到 Supabase 之前:
🌐 After initial data sync is complete, but BEFORE switching to 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 #
🌐 Step 9: Switch to Supabase
- 确保复制延迟为零:
1-- On Supabase2select * from pg_stat_subscription;3-- Check that latest_end_lsn is current- 停止向源数据库写入(如果还不是只读的话)
- 在 Supabase 取消订阅:
1DROP SUBSCRIPTION supabase_subscription;- 更新应用的连接字符串以指向 Supabase
- 验证应用功能
第10步:清理 #
🌐 Step 10: Cleanup
在源数据库上(迁移成功后):
🌐 On source database (after successful migration):
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!逻辑复制故障排除 #
🌐 Troubleshooting logical replication
| 问题 | 解决方案 |
|---|---|
| "无法连接到发布者" | 检查网络连接、防火墙规则、pg_hba.conf |
| "角色不存在" | 确保源端有复制用户,并且拥有 REPLICATION 权限 |
| "发布不存在" | 核实发布名称,并确认发布已成功创建 |
| 复制延迟增加 | 检查网络带宽、源数据库负载,增加 WAL 发送器 |
表卡在 i 状态 | 检查源表是否有锁,确认表结构匹配 |
| "复制槽已用尽" | 在 Postgres.conf 中增加 max_replication_slots |
重要的限制 #
🌐 Important limitations
- DDL 更改:模式修改不会被复制 - 迁移期间请冻结模式
- 序列:切换前需要手动同步
- 大对象 (LOBs):不进行复制 - 使用转储/恢复或存储在常规 bytea 列中
- 自定义类型:可能需要特别处理
- 用户和角色:必须在 Supabase 上手动重新创建
有关详细限制,请参见 Postgres 逻辑复制限制
🌐 For detailed restrictions, see Postgres Logical Replication Restrictions
什么时候使用哪种方法 #
🌐 When to use which method
使用 Dump/Restore 的情况:
- 停机时间可以接受
- 源是 Postgres 版本低于 10
- 更简单的流程更好
- 无法在源端配置逻辑复制
在以下情况下使用逻辑复制:
- 所需停机时间最少
- 两边都是 Postgres 10 以上
- 可以修改源配置
- 拥有复制权限
获取帮助 #
🌐 Getting help
- 对于大于 150 GB 的数据库:在开始之前请 联系 Supabase 支持
- Supabase 仪表板支持
- Supabase Discord
- Postgres 角色和权限指南
- 行级别安全指南