数据库迁移 #
数据库备份 #
手动备份 #
bash
# 创建备份
heroku pg:backups:capture
# 指定数据库
heroku pg:backups:capture DATABASE_URL
# 查看备份列表
heroku pg:backups
# 输出示例
# === Backups
# ID Created at Status Size Database
# b003 2024-01-15 15:00:00 UTC Completed 2024-01-15 15:01:00 UTC 25.5MB DATABASE_URL
# b002 2024-01-14 15:00:00 UTC Completed 2024-01-14 15:01:00 UTC 24.8MB DATABASE_URL
# b001 2024-01-13 15:00:00 UTC Completed 2024-01-13 15:01:00 UTC 23.1MB DATABASE_URL
自动备份 #
bash
# 设置每日自动备份
heroku pg:backups:schedule DATABASE_URL --at '02:00 America/Los_Angeles'
# 查看备份计划
heroku pg:backups:schedules
# 取消自动备份
heroku pg:backups:unschedule DATABASE_URL
备份保留策略 #
text
┌─────────────────────────────────────────────────────┐
│ 备份保留策略 │
├─────────────────────────────────────────────────────┤
│ │
│ Mini/Basic 计划: │
│ ├── 手动备份:保留 2 个 │
│ └── 自动备份:保留 7 天 │
│ │
│ Standard 计划: │
│ ├── 手动备份:保留 10 个 │
│ └── 自动备份:保留 30 天 │
│ │
│ Premium 计划: │
│ ├── 手动备份:保留 25 个 │
│ └── 自动备份:保留 12 个月 │
│ │
└─────────────────────────────────────────────────────┘
备份恢复 #
恢复到同一应用 #
bash
# 恢复最新备份
heroku pg:backups:restore --app myapp --confirm myapp
# 恢复指定备份
heroku pg:backups:restore b002 --app myapp --confirm myapp
# 恢复到特定数据库
heroku pg:backups:restore b002 HEROKU_POSTGRESQL_SILVER --app myapp --confirm myapp
跨应用恢复 #
bash
# 从 source-app 恢复到 target-app
heroku pg:backups:restore source-app::b002 --app target-app --confirm target-app
# 从其他应用的最新备份恢复
heroku pg:backups:restore source-app::DATABASE_URL --app target-app --confirm target-app
下载备份 #
bash
# 下载最新备份
heroku pg:backups:download
# 下载指定备份
heroku pg:backups:download b002
# 指定输出文件
heroku pg:backups:download b002 -o backup.sql
数据迁移 #
从本地迁移到 Heroku #
bash
# 方式一:使用 pg:push
heroku pg:push mylocaldb DATABASE_URL --app myapp
# 方式二:使用 pg_dump 和 psql
pg_dump mylocaldb | heroku pg:psql --app myapp
# 方式三:使用备份恢复
pg_dump mylocaldb > backup.sql
# 上传到可访问的 URL
# heroku pg:backups:restore 'https://example.com/backup.sql' --app myapp --confirm myapp
从 Heroku 迁移到本地 #
bash
# 方式一:使用 pg:pull
heroku pg:pull DATABASE_URL mylocaldb --app myapp
# 方式二:使用备份下载
heroku pg:backups:download
pg_restore -d mylocaldb backup.sql
跨应用迁移 #
bash
# 从 app-a 迁移到 app-b
heroku pg:copy app-a::DATABASE_URL DATABASE_URL --app app-b --confirm app-b
从其他云服务迁移 #
bash
# 从 AWS RDS 迁移
# 1. 导出 RDS 数据
pg_dump $RDS_URL > rds_backup.sql
# 2. 导入到 Heroku
cat rds_backup.sql | heroku pg:psql --app myapp
# 或使用 pg:push
# 先设置本地数据库指向 RDS
export DATABASE_URL=$RDS_URL
heroku pg:push mytempdb DATABASE_URL --app myapp
数据库版本升级 #
查看当前版本 #
bash
# 查看 PostgreSQL 版本
heroku pg:info
# 输出示例
# PG Version: 16.2
升级流程 #
text
┌─────────────────────────────────────────────────────┐
│ 数据库版本升级流程 │
├─────────────────────────────────────────────────────┤
│ │
│ 1. 创建新版本数据库 │
│ heroku addons:create heroku-postgresql:standard-0 --version=16 │
│ │
│ 2. 迁移数据 │
│ heroku pg:copy OLD_DB NEW_DB --app myapp │
│ │
│ 3. 验证新数据库 │
│ heroku pg:info NEW_DB │
│ │
│ 4. 切换应用使用新数据库 │
│ heroku pg:promote NEW_DB │
│ │
│ 5. 更新应用配置 │
│ heroku config:set DATABASE_URL=... │
│ │
│ 6. 删除旧数据库 │
│ heroku addons:destroy OLD_DB │
│ │
└─────────────────────────────────────────────────────┘
升级命令 #
bash
# 1. 创建新版本数据库
heroku addons:create heroku-postgresql:standard-0 --version=16 --app myapp
# 2. 迁移数据
heroku pg:copy DATABASE_URL HEROKU_POSTGRESQL_NEW_URL --app myapp --confirm myapp
# 3. 提升新数据库为主数据库
heroku pg:promote HEROKU_POSTGRESQL_NEW_URL --app myapp
# 4. 验证
heroku pg:info
# 5. 删除旧数据库
heroku addons:destroy heroku-postgresql-old
数据库分叉与跟随 #
创建分叉 #
bash
# 创建数据库分叉(用于测试/开发)
heroku addons:create heroku-postgresql:standard-0 --fork DATABASE_URL --app myapp-test
# 指定时间点分叉
heroku addons:create heroku-postgresql:standard-0 --fork DATABASE_URL --at "2024-01-15 10:00:00" --app myapp-test
创建跟随数据库 #
bash
# 创建只读副本
heroku addons:create heroku-postgresql:standard-0 --follow DATABASE_URL --app myapp
# 查看复制状态
heroku pg:info
# 输出示例
# === HEROKU_POSTGRESQL_FOLLOWER_URL
# Following: DATABASE_URL
# Behind By: 0 sec
分叉 vs 跟随 #
| 特性 | Fork | Follower |
|---|---|---|
| 数据同步 | 一次性复制 | 持续同步 |
| 读写 | 可读写 | 只读 |
| 用途 | 测试、开发 | 负载分担 |
| 成本 | 独立计费 | 独立计费 |
数据迁移脚本 #
Node.js 迁移脚本 #
javascript
// scripts/migrate-data.js
const { Pool } = require('pg');
async function migrateData() {
const sourcePool = new Pool({
connectionString: process.env.SOURCE_DATABASE_URL,
ssl: { rejectUnauthorized: false }
});
const targetPool = new Pool({
connectionString: process.env.TARGET_DATABASE_URL,
ssl: { rejectUnauthorized: false }
});
try {
// 读取源数据
const { rows: users } = await sourcePool.query('SELECT * FROM users');
// 写入目标数据库
for (const user of users) {
await targetPool.query(
'INSERT INTO users (id, email, name, created_at) VALUES ($1, $2, $3, $4) ON CONFLICT (id) DO UPDATE SET email = $2, name = $3',
[user.id, user.email, user.name, user.created_at]
);
}
console.log(`Migrated ${users.length} users`);
} finally {
await sourcePool.end();
await targetPool.end();
}
}
migrateData().catch(console.error);
Python 迁移脚本 #
python
import os
import psycopg2
from psycopg2.extras import RealDictCursor
def migrate_data():
source_conn = psycopg2.connect(
os.environ['SOURCE_DATABASE_URL'],
sslmode='require'
)
target_conn = psycopg2.connect(
os.environ['TARGET_DATABASE_URL'],
sslmode='require'
)
try:
source_cur = source_conn.cursor(cursor_factory=RealDictCursor)
target_cur = target_conn.cursor()
# 读取源数据
source_cur.execute('SELECT * FROM users')
users = source_cur.fetchall()
# 写入目标数据库
for user in users:
target_cur.execute(
'''
INSERT INTO users (id, email, name, created_at)
VALUES (%(id)s, %(email)s, %(name)s, %(created_at)s)
ON CONFLICT (id) DO UPDATE SET
email = %(email)s,
name = %(name)s
''',
user
)
target_conn.commit()
print(f'Migrated {len(users)} users')
finally:
source_conn.close()
target_conn.close()
if __name__ == '__main__':
migrate_data()
数据验证 #
数据一致性检查 #
bash
# 检查表数量
heroku pg:psql -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public'"
# 检查行数
heroku pg:psql -c "SELECT schemaname, relname, n_live_tup FROM pg_stat_user_tables"
# 检查数据完整性
heroku pg:psql -c "SELECT table_name, pg_size_pretty(pg_total_relation_size(table_name::text)) FROM information_schema.tables WHERE table_schema = 'public'"
迁移后验证脚本 #
javascript
// scripts/verify-migration.js
const { Pool } = require('pg');
async function verifyMigration() {
const sourcePool = new Pool({
connectionString: process.env.SOURCE_DATABASE_URL,
ssl: { rejectUnauthorized: false }
});
const targetPool = new Pool({
connectionString: process.env.TARGET_DATABASE_URL,
ssl: { rejectUnauthorized: false }
});
try {
const tables = ['users', 'posts', 'comments'];
for (const table of tables) {
const sourceCount = (await sourcePool.query(`SELECT COUNT(*) FROM ${table}`)).rows[0].count;
const targetCount = (await targetPool.query(`SELECT COUNT(*) FROM ${table}`)).rows[0].count;
if (sourceCount === targetCount) {
console.log(`✓ ${table}: ${sourceCount} rows`);
} else {
console.log(`✗ ${table}: source=${sourceCount}, target=${targetCount}`);
}
}
} finally {
await sourcePool.end();
await targetPool.end();
}
}
verifyMigration().catch(console.error);
最佳实践 #
迁移前检查清单 #
markdown
## 迁移前检查
- [ ] 备份源数据库
- [ ] 验证目标数据库配置
- [ ] 检查数据大小和预估时间
- [ ] 通知相关团队
- [ ] 准备回滚计划
## 迁移中监控
- [ ] 监控迁移进度
- [ ] 检查错误日志
- [ ] 验证数据完整性
## 迁移后验证
- [ ] 验证数据数量
- [ ] 验证数据完整性
- [ ] 测试应用功能
- [ ] 更新文档
零停机迁移 #
bash
# 1. 创建跟随数据库
heroku addons:create heroku-postgresql:standard-0 --follow DATABASE_URL
# 2. 等待同步完成
heroku pg:info
# 3. 提升跟随数据库为主数据库
heroku pg:promote HEROKU_POSTGRESQL_FOLLOWER_URL
# 4. 应用自动切换到新数据库
大数据量迁移 #
bash
# 分批迁移
# 使用 LIMIT 和 OFFSET
# 或使用游标
heroku pg:psql -c "DECLARE cur CURSOR FOR SELECT * FROM large_table"
heroku pg:psql -c "FETCH 1000 FROM cur"
故障排查 #
迁移失败 #
bash
# 检查错误日志
heroku logs --tail | grep migration
# 检查数据库状态
heroku pg:info
# 常见问题
# 1. 连接超时 - 增加超时时间
# 2. 磁盘空间不足 - 升级计划
# 3. 权限问题 - 检查用户权限
数据不一致 #
bash
# 重新迁移特定表
heroku pg:psql -c "TRUNCATE TABLE users"
heroku pg:psql < users_backup.sql
# 或使用 COPY
heroku pg:psql -c "COPY users FROM STDIN WITH CSV" < users.csv
下一步 #
数据库迁移掌握后,接下来学习 Heroku Redis 了解缓存配置!
最后更新:2026-03-28