Files
taiji-AI-PAD/PRODUCTION_DATABASE_SYNC_GUIDE.md
T
2026-03-13 02:53:27 +00:00

663 lines
18 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.
# 生产环境数据库同步指南
生成时间: 2026-03-12
测试环境数据库: `taiji`
生产环境数据库: `taiji_prod`
## 📋 迁移总览
测试环境在 2026年3月9-12日期间执行了 **8个数据库迁移**,需要同步到生产环境:
| 迁移编号 | 文件名 | 类型 | 影响范围 | 风险等级 |
|---------|--------|------|----------|---------|
| 017 | add_external_data_tools.sql | 新增表 | 外部数据工具功能 | 🟢 低 |
| 018 | add_external_toolkits.sql | 新增表 | 外部工具集功能 | 🟢 低 |
| 019 | add_agent_model_name.sql | 字段新增 | `agents`、`agent_billing_records` | 🟢 低 |
| 020 | fix_quota_defaults.sql | 字段修改 | 配额表(3个表) | 🟡 中 |
| 021 | fix_agent_type_length.sql | 字段修改 | `agent_billing_records` | 🟢 低 |
| 022 | fix_eu_equals_cost.sql | 字段类型+数据修正 | `agent_billing_records`、`model_billing_records` | 🟡 中 |
| 023 | fix_billing_channel_id.sql | 数据修正 | `agent_billing_records`、`model_billing_records` | 🟢 低 |
| 024 | add_record_type.sql | 字段新增+数据修正 | `agent_billing_records` | 🟡 中 |
---
## 📝 详细迁移内容
### 迁移 017: 添加外部数据工具表
**文件**: `services/mcp-server/migrations/017_add_external_data_tools.sql`
**目的**: 支持用户创建和管理自定义的外部API数据工具
**操作**:
```sql
-- 创建表
CREATE TABLE external_data_tools (
id UUID PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT,
url VARCHAR(500) NOT NULL,
method VARCHAR(10) DEFAULT 'POST',
auth_type VARCHAR(20) DEFAULT 'none',
tool_ref_id VARCHAR(100) UNIQUE,
status VARCHAR(20) DEFAULT 'pending',
error_message TEXT,
owner_id UUID NOT NULL REFERENCES users(id),
tenant_id UUID REFERENCES users(id),
channel_id UUID REFERENCES channels(id),
is_active BOOLEAN DEFAULT TRUE,
usage_count INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 索引
CREATE INDEX idx_external_data_tool_owner ON external_data_tools(owner_id);
CREATE INDEX idx_external_data_tool_ref ON external_data_tools(tool_ref_id);
CREATE INDEX idx_external_data_tool_status ON external_data_tools(status);
```
**影响**: 新功能,对现有数据无影响
**回滚方案**:
```sql
DROP TABLE IF EXISTS external_data_tools CASCADE;
```
---
### 迁移 018: 添加外部数据工具集表
**文件**: `services/mcp-server/migrations/018_add_external_toolkits.sql`
**目的**: 允许用户将多个外部工具组合成工具集,方便部署自定义Agent
**操作**:
```sql
-- 创建表
CREATE TABLE external_toolkits (
id UUID PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT,
tool_ids JSONB NOT NULL DEFAULT '[]'::jsonb,
owner_id UUID NOT NULL REFERENCES users(id),
tenant_id UUID REFERENCES users(id),
channel_id UUID REFERENCES channels(id),
is_active BOOLEAN DEFAULT TRUE,
usage_count INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uq_toolkit_name_owner UNIQUE (name, owner_id)
);
-- 索引
CREATE INDEX idx_external_toolkit_owner ON external_toolkits(owner_id);
```
**影响**: 新功能,对现有数据无影响
**回滚方案**:
```sql
DROP TABLE IF EXISTS external_toolkits CASCADE;
```
---
### 迁移 019: 为Agent添加模型名称字段 ⭐ 重要
**文件**: `services/mcp-server/migrations/019_add_agent_model_name.sql`
**目的**:
- 记录每个Agent使用的LLM模型(如 `azure/gpt-4`, `gemini/gemini-pro`)
- 支持计费记录中追踪模型使用情况
**操作**:
```sql
-- 1. agents 表添加 model_name 字段
ALTER TABLE agents ADD COLUMN IF NOT EXISTS model_name VARCHAR(100);
CREATE INDEX IF NOT EXISTS idx_agent_model_name ON agents(model_name);
-- 2. agent_billing_records 表添加 model_name 字段
ALTER TABLE agent_billing_records ADD COLUMN IF NOT EXISTS model_name VARCHAR(100);
CREATE INDEX IF NOT EXISTS idx_agent_billing_model_name ON agent_billing_records(model_name);
```
**影响**:
- 现有Agent的 `model_name` 为 NULL(可接受)
- 新创建的Agent将记录模型信息
**注意事项**:
- ⚠️ 如果生产环境已有该字段,使用 `IF NOT EXISTS` 将安全跳过
- 该字段为 **可空**,不强制现有记录填充
**回滚方案**:
```sql
ALTER TABLE agents DROP COLUMN IF EXISTS model_name;
ALTER TABLE agent_billing_records DROP COLUMN IF EXISTS model_name;
DROP INDEX IF EXISTS idx_agent_model_name;
DROP INDEX IF EXISTS idx_agent_billing_model_name;
```
---
### 迁移 020: 修复配额字段默认值 ⚠️ 需要谨慎
**文件**: `services/mcp-server/migrations/020_fix_quota_defaults.sql`
**目的**:
- 消除配额字段的 NULL 值,避免计算错误
- 添加 NOT NULL 约束和检查约束
**操作**:
```sql
-- 1. tenant_custom_agent_quotas 表
ALTER TABLE tenant_custom_agent_quotas
ALTER COLUMN cpu_quota SET DEFAULT 0, ALTER COLUMN cpu_quota SET NOT NULL,
ALTER COLUMN memory_quota SET DEFAULT 0, ALTER COLUMN memory_quota SET NOT NULL,
ALTER COLUMN cpu_used SET DEFAULT 0, ALTER COLUMN cpu_used SET NOT NULL,
ALTER COLUMN memory_used SET DEFAULT 0, ALTER COLUMN memory_used SET NOT NULL,
ALTER COLUMN agent_count SET DEFAULT 0, ALTER COLUMN agent_count SET NOT NULL;
UPDATE tenant_custom_agent_quotas SET cpu_quota = 0 WHERE cpu_quota IS NULL;
UPDATE tenant_custom_agent_quotas SET memory_quota = 0 WHERE memory_quota IS NULL;
UPDATE tenant_custom_agent_quotas SET cpu_used = 0 WHERE cpu_used IS NULL;
UPDATE tenant_custom_agent_quotas SET memory_used = 0 WHERE memory_used IS NULL;
UPDATE tenant_custom_agent_quotas SET agent_count = 0 WHERE agent_count IS NULL;
-- 添加检查约束
ALTER TABLE tenant_custom_agent_quotas
ADD CONSTRAINT chk_tenant_cpu_used CHECK (cpu_used <= cpu_quota),
ADD CONSTRAINT chk_tenant_memory_used CHECK (memory_used <= memory_quota);
-- 2. channel_custom_agent_quotas 表(类似操作)
-- 3. platform_agent_quotas 表(类似操作)
```
**影响**:
- ⚠️ **数据修改**: 将所有 NULL 值更新为 0
- ⚠️ **约束添加**: 新增检查约束,使用量不能超过配额
**⚠️ 执行前检查**:
```sql
-- 检查生产环境是否有 NULL 值
SELECT
COUNT(*) as total_records,
COUNT(CASE WHEN cpu_quota IS NULL THEN 1 END) as null_cpu_quota,
COUNT(CASE WHEN memory_quota IS NULL THEN 1 END) as null_memory_quota
FROM tenant_custom_agent_quotas;
-- 检查是否有使用量超过配额的记录(会导致约束添加失败)
SELECT * FROM tenant_custom_agent_quotas WHERE cpu_used > cpu_quota;
SELECT * FROM tenant_custom_agent_quotas WHERE memory_used > memory_quota;
```
**回滚方案**:
```sql
-- 删除约束(如果需要)
ALTER TABLE tenant_custom_agent_quotas DROP CONSTRAINT IF EXISTS chk_tenant_cpu_used;
ALTER TABLE tenant_custom_agent_quotas DROP CONSTRAINT IF EXISTS chk_tenant_memory_used;
-- 注意:无法回滚 NOT NULL 约束和数据修改
```
---
### 迁移 021: 修复agent_type字段长度
**文件**: `services/mcp-server/migrations/021_fix_agent_type_length.sql`
**目的**: 修复 `microsoft_learn_agent`(22字符)超出 VARCHAR(20) 限制的问题
**操作**:
```sql
ALTER TABLE agent_billing_records ALTER COLUMN agent_type TYPE VARCHAR(100);
```
**影响**: 扩展字段长度,对现有数据无负面影响
**测试建议**:
```sql
-- 检查是否有被截断的数据
SELECT agent_type, LENGTH(agent_type) as len, COUNT(*)
FROM agent_billing_records
GROUP BY agent_type
ORDER BY len DESC;
```
**回滚方案**:
```sql
-- 仅在确认所有值都 ≤20 字符时才能回滚
ALTER TABLE agent_billing_records ALTER COLUMN agent_type TYPE VARCHAR(20);
```
---
### 迁移 022: 修复EU计算逻辑 ⚠️ 重要数据修正
**文件**: `services/mcp-server/migrations/022_fix_eu_equals_cost.sql`
**目的**:
- **旧逻辑**: `EU = ceil(duration_seconds / 10)`,即 1 EU = 10秒
- **新逻辑**: `EU = Cost(美元)`,即 1 EU = 1 美元
- 将历史数据的 `eu_consumed` 修正为 `cost` 的值
**操作**:
```sql
-- 1. 修改字段类型以支持小数
ALTER TABLE agent_billing_records
ALTER COLUMN eu_consumed TYPE NUMERIC(12, 4);
-- 2. 更新历史数据
UPDATE agent_billing_records
SET eu_consumed = cost
WHERE cost IS NOT NULL AND cost > 0;
UPDATE model_billing_records
SET eu_consumed = total_cost
WHERE total_cost IS NOT NULL AND total_cost > 0;
-- 3. 添加注释说明
COMMENT ON COLUMN agent_billing_records.eu_consumed IS 'EU 消耗(1 EU = 1 美元,EU = Cost)';
```
**影响**:
- ⚠️ **修改所有历史计费记录**的 `eu_consumed` 值
- 会导致历史数据的EU统计发生变化
**⚠️ 执行前备份**:
```sql
-- 备份历史数据
CREATE TABLE agent_billing_records_backup_20260312 AS
SELECT * FROM agent_billing_records;
CREATE TABLE model_billing_records_backup_20260312 AS
SELECT * FROM model_billing_records;
```
**验证**:
```sql
-- 检查 EU 和 Cost 是否一致
SELECT id, agent_name, eu_consumed, cost,
CASE WHEN ABS(eu_consumed - cost) < 0.0001 THEN 'OK' ELSE 'MISMATCH' END as status
FROM agent_billing_records
WHERE cost > 0
LIMIT 20;
```
**回滚方案**:
```sql
-- 从备份表恢复
UPDATE agent_billing_records abr
SET eu_consumed = backup.eu_consumed
FROM agent_billing_records_backup_20260312 backup
WHERE abr.id = backup.id;
```
---
### 迁移 023: 修复计费记录中缺失的channel_id
**文件**: `services/mcp-server/migrations/023_fix_billing_channel_id.sql`
**目的**: 修复计费记录创建时未正确设置 `channel_id` 的问题
**操作**:
```sql
-- 1. 更新 AgentBillingRecord
UPDATE agent_billing_records abr
SET channel_id = u.channel_id
FROM users u
WHERE abr.user_id = u.id
AND abr.channel_id IS NULL
AND u.channel_id IS NOT NULL;
-- 2. 更新 ModelBillingRecord
UPDATE model_billing_records mbr
SET channel_id = u.channel_id
FROM users u
WHERE mbr.tenant_id = u.id
AND mbr.channel_id IS NULL
AND u.channel_id IS NOT NULL;
```
**影响**: 补充缺失的 `channel_id`,提高数据完整性
**执行前检查**:
```sql
-- 检查有多少记录缺失 channel_id
SELECT
'AgentBillingRecord' as table_name,
COUNT(*) as total,
COUNT(channel_id) as with_channel,
COUNT(*) - COUNT(channel_id) as without_channel
FROM agent_billing_records
UNION ALL
SELECT
'ModelBillingRecord' as table_name,
COUNT(*) as total,
COUNT(channel_id) as with_channel,
COUNT(*) - COUNT(channel_id) as without_channel
FROM model_billing_records;
```
**回滚方案**: 无需回滚(数据修正)
---
### 迁移 024: 添加record_type字段 ⚠️ 重要业务逻辑变更
**文件**: `services/mcp-server/migrations/024_add_record_type.sql`
**目的**: 区分两种计费方式
- `vm_runtime`: VM运行时间计费(一个Agent = 一条记录,周期性更新)
- `api_call`: API调用计费(每次调用 = 一条新记录)
**操作**:
```sql
-- 1. 添加字段
ALTER TABLE agent_billing_records
ADD COLUMN IF NOT EXISTS record_type VARCHAR(20) NOT NULL DEFAULT 'vm_runtime';
-- 2. 推断现有记录类型
UPDATE agent_billing_records
SET record_type = 'api_call'
WHERE request_id IS NOT NULL
AND request_id != ''
AND end_time IS NOT NULL;
-- 3. 添加索引
CREATE INDEX IF NOT EXISTS idx_agent_billing_record_type
ON agent_billing_records(record_type);
CREATE INDEX IF NOT EXISTS idx_agent_billing_vm_runtime
ON agent_billing_records(record_type, end_time)
WHERE record_type = 'vm_runtime' AND end_time IS NULL;
```
**影响**:
- 新增业务逻辑字段
- 周期计费任务将只更新 `record_type='vm_runtime'` 的记录
**验证**:
```sql
SELECT
record_type,
COUNT(*) as count,
COUNT(CASE WHEN end_time IS NULL THEN 1 END) as running_count,
COUNT(CASE WHEN end_time IS NOT NULL THEN 1 END) as completed_count
FROM agent_billing_records
GROUP BY record_type;
```
**回滚方案**:
```sql
ALTER TABLE agent_billing_records DROP COLUMN IF EXISTS record_type;
DROP INDEX IF EXISTS idx_agent_billing_record_type;
DROP INDEX IF EXISTS idx_agent_billing_vm_runtime;
```
---
## 🚀 执行顺序和依赖关系
迁移必须按以下顺序执行(有依赖关系):
```
017 ─────┐
├──→ 可并行执行
018 ─────┘
019 ─→ 020 ─→ 021 ─→ 022 ─→ 023 ─→ 024
↑ ↑
│ │
修改配额约束 修改计费逻辑(最关键)
```
**推荐分批执行**:
- **第一批(新功能)**: 017, 018, 019
- **第二批(数据修正)**: 020, 021, 023
- **第三批(核心逻辑)**: 022, 024
---
## ⚙️ 执行步骤
### 步骤 1: 备份生产数据库 🔴 必做
```bash
# 使用项目提供的备份脚本
cd /home/taiji/tools/taiji-AI-PAD
bash scripts/backup_postgres.sh
# 或手动备份
kubectl exec -n taiji-ai-pad <postgres-pod> -- \
pg_dump -U postgres taiji_prod > backup_taiji_prod_$(date +%Y%m%d_%H%M%S).dump
```
### 步骤 2: 检查生产环境状态
```bash
# 检查数据库连接
python services/mcp-server/check_prod_database_status.py
# 检查计费健康状态
python services/mcp-server/check_billing_health.py
# 检查配额数据
python services/mcp-server/check_quota_data.py
```
### 步骤 3: 使用自动同步脚本(推荐)
```bash
# 使用交互式同步脚本
bash scripts/sync_prod_database.sh
```
该脚本会:
- ✅ 自动检查K8s集群连接
- ✅ 显示当前数据库配置
- ✅ 逐个执行迁移,每步都需要确认
- ✅ 记录执行日志
### 步骤 4: 手动执行(如果需要更细粒度控制)
```bash
# 进入mcp-server Pod
POD_NAME=$(kubectl get pods -n taiji-ai-pad -l app=mcp-server -o jsonpath='{.items[0].metadata.name}')
kubectl exec -it -n taiji-ai-pad $POD_NAME -- bash
# 切换到migrations目录
cd /app/migrations
# 执行迁移(按顺序)
python run_017_migration.py
python run_018_migration.py
python run_019_migration.py
python run_020_fix_quota_defaults.py
# ... 依次执行
```
### 步骤 5: 验证结果
```bash
# 检查表结构
kubectl exec -n taiji-ai-pad <postgres-pod> -- \
psql -U postgres -d taiji_prod -c "\d agent_billing_records"
# 检查数据完整性
kubectl exec -n taiji-ai-pad <postgres-pod> -- \
psql -U postgres -d taiji_prod -c "
SELECT
COUNT(*) as total_records,
COUNT(record_type) as with_record_type,
COUNT(model_name) as with_model_name,
COUNT(channel_id) as with_channel_id
FROM agent_billing_records;
"
# 检查EU和Cost的一致性
kubectl exec -n taiji-ai-pad <postgres-pod> -- \
psql -U postgres -d taiji_prod -c "
SELECT
COUNT(*) as total,
COUNT(CASE WHEN ABS(eu_consumed - cost) < 0.0001 THEN 1 END) as consistent,
COUNT(CASE WHEN ABS(eu_consumed - cost) >= 0.0001 THEN 1 END) as inconsistent
FROM agent_billing_records
WHERE cost > 0;
"
```
### 步骤 6: 重启服务(如果需要)
```bash
# 重启mcp-server以应用新配置
kubectl rollout restart deployment/mcp-server -n taiji-ai-pad
# 等待Pod就绪
kubectl rollout status deployment/mcp-server -n taiji-ai-pad
```
---
## ⚠️ 风险和注意事项
### 🔴 高风险操作
1. **迁移 022(EU计算逻辑修改)**
- 会修改所有历史计费记录
- 建议在业务低峰期执行
- 必须先备份
2. **迁移 020(配额约束)**
- 会添加检查约束
- 如果有数据不一致(使用量>配额),迁移会失败
- 需要先修复数据
### 🟡 中风险操作
1. **迁移 024(record_type字段)**
- 修改了计费逻辑
- 需要确保周期计费任务已更新代码
### 🟢 低风险操作
- 迁移 017, 018, 019, 021, 023
- 这些主要是新增字段/表,对现有业务无影响
---
## 🔄 回滚计划
如果迁移后发现问题,按以下步骤回滚:
### 快速回滚(恢复备份)
```bash
# 停止服务
kubectl scale deployment/mcp-server -n taiji-ai-pad --replicas=0
# 恢复数据库
kubectl exec -n taiji-ai-pad <postgres-pod> -- \
psql -U postgres -c "DROP DATABASE taiji_prod;"
kubectl exec -n taiji-ai-pad <postgres-pod> -- \
psql -U postgres -c "CREATE DATABASE taiji_prod;"
kubectl exec -i -n taiji-ai-pad <postgres-pod> -- \
psql -U postgres taiji_prod < backup_taiji_prod_20260312_HHMMSS.dump
# 重启服务
kubectl scale deployment/mcp-server -n taiji-ai-pad --replicas=1
```
### 部分回滚(单个迁移)
参考每个迁移的"回滚方案"章节,执行对应的 SQL 语句。
---
## 📊 预期影响评估
### 数据量影响
```sql
-- 评估受影响的记录数
SELECT
'agent_billing_records' as table_name,
COUNT(*) as total_records,
pg_size_pretty(pg_total_relation_size('agent_billing_records')) as table_size
FROM agent_billing_records
UNION ALL
SELECT
'model_billing_records' as table_name,
COUNT(*) as total_records,
pg_size_pretty(pg_total_relation_size('model_billing_records')) as table_size
FROM model_billing_records;
```
### 停机时间估算
- **新表创建(017, 018)**: < 1秒
- **字段添加(019, 021, 024)**: 1-5秒(取决于表大小)
- **数据修正(020, 022, 023)**: 1-10分钟(取决于记录数)
**建议总停机时间**: 15-30分钟(保守估计)
---
## ✅ 完成检查清单
执行完成后,请确认:
- [ ] 所有迁移脚本执行成功,无错误
- [ ] 新表已创建:`external_data_tools`, `external_toolkits`
- [ ] 新字段已添加:`model_name`, `record_type`
- [ ] `eu_consumed` 和 `cost` 数据一致
- [ ] `channel_id` 缺失值已修复
- [ ] 配额约束已添加且无冲突
- [ ] 服务正常启动,无报错
- [ ] 健康检查通过
- [ ] 计费任务正常运行
- [ ] 备份文件已妥善保存
---
## 📞 问题反馈
如果迁移过程中遇到问题,请检查:
1. **Pod日志**:
```bash
kubectl logs -n taiji-ai-pad deployment/mcp-server --tail=100
```
2. **数据库日志**:
```bash
kubectl logs -n taiji-ai-pad <postgres-pod> --tail=100
```
3. **运行健康检查**:
```bash
python services/mcp-server/check_billing_health.py
```
---
## 📚 相关文档
- [计费系统安全性分析](./BILLING_SECURITY_ANALYSIS.md)
- [数据库配置文档](./config/database-config.md)
- [计费系统架构文档](./Docs/项目文档/计费管理三维度接口文档.md)
---
## 📅 变更记录
| 日期 | 操作人 | 环境 | 迁移 | 结果 | 备注 |
|------|--------|------|------|------|------|
| 2026-03-09~12 | - | 测试环境 | 017-024 | ✅ 成功 | 初次执行 |
| _待填写_ | _待填写_ | 生产环境 | 017-024 | _待填写_ | _待填写_ |
---
**最后更新**: 2026-03-12
**文档版本**: v1.0