Files
MoviePilot/docs/postgresql-setup.md
2026-08-10 06:53:07 +08:00

309 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PostgreSQL 数据库配置指南
MoviePilot 现在支持 PostgreSQL 数据库,您可以根据需要选择使用 SQLite 或 PostgreSQL。
## 配置选项
### 1. 数据库类型选择
`config/app.env` 文件中设置:
```bash
# 使用 SQLite默认
DB_TYPE=sqlite
# 使用 PostgreSQL
DB_TYPE=postgresql
```
### 2. PostgreSQL 配置参数
`DB_TYPE=postgresql` 时,以下配置生效:
```bash
# PostgreSQL 主机地址
DB_POSTGRESQL_HOST=localhost
# PostgreSQL 端口;使用 Unix Socket 时可留空
DB_POSTGRESQL_PORT=5432
# PostgreSQL 数据库名
DB_POSTGRESQL_DATABASE=moviepilot
# PostgreSQL 用户名
DB_POSTGRESQL_USERNAME=moviepilot
# PostgreSQL 密码
DB_POSTGRESQL_PASSWORD=moviepilot
# PostgreSQL 连接池大小
DB_POSTGRESQL_POOL_SIZE=20
# PostgreSQL 连接池溢出数量
DB_POSTGRESQL_MAX_OVERFLOW=30
```
### 3. Unix Socket 连接
如果 PostgreSQL 通过 Unix Socket 暴露,可以把 `DB_POSTGRESQL_HOST` 设置为套接字目录。
```bash
DB_TYPE=postgresql
DB_POSTGRESQL_HOST=/var/run/postgresql
DB_POSTGRESQL_PORT=
DB_POSTGRESQL_DATABASE=moviepilot
DB_POSTGRESQL_USERNAME=moviepilot
DB_POSTGRESQL_PASSWORD=moviepilot
```
如需显式指定 socket 端口,也可以保留 `DB_POSTGRESQL_PORT`,程序会生成带 `host=/path/to/socket` 查询参数的 PostgreSQL URL。
## Docker 部署
### 使用外部 PostgreSQL
如果您想使用外部的 PostgreSQL 服务:
1. 确保外部 PostgreSQL 服务已启动并可访问
2. 设置环境变量指向外部服务:
```bash
DB_TYPE=postgresql
DB_POSTGRESQL_HOST=your-postgresql-host
DB_POSTGRESQL_PORT=5432
DB_POSTGRESQL_DATABASE=moviepilot
DB_POSTGRESQL_USERNAME=your-username
DB_POSTGRESQL_PASSWORD=your-password
```
## 数据迁移
### 从 SQLite 迁移到 PostgreSQL以在 Windows 下操作为例)
1. 关闭 SQLite 的 WAL 模式(如果此前已经开启),并关闭 MoviePilot
2. 备份现有的 SQLite 数据库文件(`config/user.db`
3. 按照上述要求修改配置为 PostgreSQL
4. 注意,由于 SQLite 与 PostgreSQL 对部分字段的类型(例如`json`类型)定义不同,请勿通过`user.db`在迁移阶段直接在空数据库的基础上创建表结构,而应按照下一条要求通过 MoviePilot 的初始化自动创建正确的表结构,只有在这种情况下迁移工具才能正确处理数据类型
5. 启动应用,让 MoviePilot 自动创建表结构,确认创建完成后关闭 MoviePilot
6. 使用如下 SQL 语句清理所有初始化完成的表数据,只保留表结构,避免默认的初始化数据干扰迁移
```sql
TRUNCATE TABLE agentchat, agenttask, alembic_version, downloadfailure, downloadfiles, downloadhistory, mediaserveritem, message, passkey, plugindata, site, siteicon, sitestatistic, siteuserdata, subscribe, subscribehistory, systemconfig, transferhistory, "user", userconfig, workflow RESTART IDENTITY CASCADE;
```
6. 安装 Java 21 或更新的版本,并下载 dimitri/pgloader 中的 v4 版本 jar 包,即`pgloader.jar`
7. 创建`migrate.load`文件并编辑如下内容注意Windows 下本地`.db`文件路径引用需要有 3 个斜线;`userrequest`表已经废弃,是 v1 阶段的残留物,下列配置文件将会自动去除
```
LOAD DATABASE
FROM sqlite:///X:/path/to/user.db
INTO postgresql://moviepilot:your-password@host:5432/moviepilot
WITH
data only,
reset sequences
EXCLUDING TABLE NAMES LIKE 'userrequest'
SET work_mem TO '16MB',
maintenance_work_mem TO '512MB'
CAST
type integer to boolean when (= precision 1)
;
```
8. 使用`java --enable-native-access=ALL-UNNAMED -jar .\pgloader.jar .\migrate.load`启动迁移。迁移过程中可能产生大量警告和报错,主要与孤儿索引`idx__PRIMARY`相关,形如`WARN [main] pgloader.core - Primary Keys failed (skipping): ERROR: index "idx__PRIMARY" does not belong to table "plugindata" 浣嶇疆锛?9`
9. 清理孤儿索引:`DROP INDEX IF EXISTS "idx__PRIMARY";`
10. 修正 Alembic 版本号:`UPDATE alembic_version SET version_num = 'd58298a0879f';`
11. 由于 SQLite 与 PostgreSQL 的自增序列原理不同,直接导入会出现冲突,需手动更新自增序列,执行:
```sql
-- MoviePilot PostgreSQL 自增序列修复脚本
-- 用途:修复从 SQLite 迁移到 PostgreSQL 后的自增 ID 冲突
-- 问题背景:
-- SQLite 的 AUTOINCREMENT 不依赖独立序列对象,数据行带着具体 id 值进入 PostgreSQL。
-- 但 PostgreSQL 使用独立的 Sequence如 plugindata_id_seq生成新 ID。
-- 迁移后 Sequence 仍停留在初始值(如 1而表中已有 id=10186 的数据,
-- 导致新插入时报 "duplicate key value violates unique constraint"。
-- 执行时机:
-- 1. 迁移完成后、应用启动前(必做)
-- 2. 运行中报主键冲突时(应急)
-- 3. 从备份恢复数据后(建议做)
-- 安全说明:
-- 本脚本只读表内最大 id 并调整序列,不修改任何数据行,可重复执行。
DO $$
DECLARE
rec RECORD;
seq_name TEXT;
max_id BIGINT;
tbl_full TEXT;
fix_count INT := 0;
err_count INT := 0;
BEGIN
RAISE NOTICE '开始扫描 public schema 下的自增序列...';
-- 遍历 public schema 下所有带自增序列的列
-- 通过 pg_get_serial_sequence() 直接获取序列名,不依赖 pg_depend兼容性好
FOR rec IN
SELECT
n.nspname AS schema_name,
c.relname AS table_name,
a.attname AS column_name
FROM pg_class c
JOIN pg_namespace n ON c.relnamespace = n.oid
JOIN pg_attribute a ON a.attrelid = c.oid
WHERE n.nspname = 'public'
AND c.relkind = 'r' -- 只处理普通表,排除视图/系统表
AND a.attnum > 0 -- attnum <= 0 是系统隐藏列
AND NOT a.attisdropped -- 排除已删除但未清理的列
AND c.relname NOT LIKE 'pg_%' -- 排除 PostgreSQL 系统表
AND c.relname NOT LIKE 'sql_%'
AND pg_get_serial_sequence(
quote_ident(n.nspname) || '.' || quote_ident(c.relname),
a.attname
) IS NOT NULL -- 只保留确实有自增序列关联的列
ORDER BY c.relname, a.attname
LOOP
BEGIN
-- 构造带 schema 前缀的表名quote_ident 自动处理 user 等关键字
tbl_full := quote_ident(rec.schema_name) || '.' || quote_ident(rec.table_name);
-- 获取该列当前最大值。COALESCE 处理空表(无数据时返回 0
EXECUTE format('SELECT COALESCE(MAX(%I), 0) FROM %s', rec.column_name, tbl_full)
INTO max_id;
-- 获取序列完整名称(含 schema
seq_name := pg_get_serial_sequence(tbl_full, rec.column_name);
-- 重置序列:
-- 参数1: 序列名
-- 参数2: 重置到的值max_id + 1空表时为 1
-- 参数3: is_called = false表示 nextval() 下次直接返回该值,不再递增
-- 效果:下一条 INSERT 拿到的 id 正好是表中未使用的最小值
PERFORM setval(seq_name, max_id + 1, false);
RAISE NOTICE '已修复: %.% (列 %) -> 序列 % 设为 % (表内最大 id = %)',
rec.schema_name, rec.table_name, rec.column_name,
seq_name, max_id + 1, max_id;
fix_count := fix_count + 1;
EXCEPTION WHEN OTHERS THEN
-- 单表异常不阻断整体流程,记录后继续下一张表
RAISE NOTICE '跳过 %.%: %', rec.schema_name, rec.table_name, SQLERRM;
err_count := err_count + 1;
END;
END LOOP;
RAISE NOTICE '完成。修复 % 个序列,跳过/错误 % 个。', fix_count, err_count;
END $$;
-- 验证:查看所有序列当前值,确认 last_value 大于对应表的最大 id
SELECT
sequencename,
last_value
FROM pg_sequences
WHERE schemaname = 'public'
ORDER BY sequencename;
```
12. 启动 MoviePilot如果迁移成功你应当在日志中看到类似下面的信息
```
INFO: [moviepilot] 5b3355c964bb_2_2_0.py - 发现 21 个表需要检查序列
INFO: [moviepilot] a946dae52526_2_2_1.py - 开始PostgreSQL数据库userid字段迁移...
INFO: [moviepilot] a946dae52526_2_2_1.py - PostgreSQL数据库userid字段迁移完成
INFO: [moviepilot] 41ef1dd7467c_2_2_2.py - SystemConfig 表去重操作已完成。
INFO: Started server process [129]
```
### 从 PostgreSQL 迁移到 SQLite
1. 导出 PostgreSQL 数据
2. 修改配置为 SQLite
3. 启动应用,数据库表会自动创建
4. 导入数据到 SQLite
## 数据备份
### PostgreSQL 数据备份
PostgreSQL 数据存储在 `${CONFIG_DIR}/postgresql/` 目录中,您可以通过以下方式进行备份:
#### 1. 文件级备份
```bash
# 备份整个PostgreSQL数据目录
tar -czf postgresql_backup_$(date +%Y%m%d_%H%M%S).tar.gz config/postgresql/
```
#### 2. 数据库级备份
```bash
# 进入容器
docker exec -it moviepilot bash
# 使用pg_dump备份
pg_dump -h localhost -U moviepilot -d moviepilot > /config/moviepilot_backup.sql
# 或使用pg_dumpall备份所有数据库
pg_dumpall -h localhost -U moviepilot > /config/all_databases_backup.sql
```
#### 3. 恢复数据
```bash
# 恢复单个数据库
psql -h localhost -U moviepilot -d moviepilot < /config/moviepilot_backup.sql
# 恢复所有数据库
psql -h localhost -U moviepilot < /config/all_databases_backup.sql
```
## 性能优化
### PostgreSQL 优化建议
1. **连接池配置**
- 根据应用负载调整 `DB_POSTGRESQL_POOL_SIZE`
- 设置合适的 `DB_POSTGRESQL_MAX_OVERFLOW`
2. **数据库配置**
- 调整 `shared_buffers`
- 配置 `work_mem`
- 设置合适的 `maintenance_work_mem`
3. **索引优化**
- 为常用查询字段添加索引
- 定期执行 `VACUUM``ANALYZE`
## 故障排除
### 常见问题
1. **连接失败**
- 检查 PostgreSQL 服务是否启动
- 验证连接参数是否正确
- 确认网络连接和防火墙设置
2. **权限问题**
- 确保用户有足够的数据库权限
- 检查 `pg_hba.conf` 配置
3. **性能问题**
- 监控连接池使用情况
- 检查慢查询日志
- 优化数据库配置
### 日志查看
PostgreSQL 相关日志可以在以下位置查看:
- Docker 容器:`${CONFIG_DIR}/postgresql/logs/`
- 系统日志:`journalctl -u postgresql`
## 注意事项
1. **兼容性**PostgreSQL 支持从 MoviePilot v2.0 开始
2. **备份**:建议定期备份数据库
3. **版本**:建议使用 PostgreSQL 12 或更高版本
4. **字符集**:确保使用 UTF-8 字符集
## 技术支持
如果遇到问题,请:
1. 查看应用日志
2. 检查 PostgreSQL 日志
3. 在 GitHub Issues 中报告问题