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

18 KiB
Raw Permalink Blame History

生产环境数据库同步指南

生成时间: 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数据工具

操作:

-- 创建表
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);

影响: 新功能,对现有数据无影响

回滚方案:

DROP TABLE IF EXISTS external_data_tools CASCADE;

迁移 018: 添加外部数据工具集表

文件: services/mcp-server/migrations/018_add_external_toolkits.sql

目的: 允许用户将多个外部工具组合成工具集,方便部署自定义Agent

操作:

-- 创建表
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);

影响: 新功能,对现有数据无影响

回滚方案:

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)
  • 支持计费记录中追踪模型使用情况

操作:

-- 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 将安全跳过
  • 该字段为 可空,不强制现有记录填充

回滚方案:

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 约束和检查约束

操作:

-- 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
  • ⚠️ 约束添加: 新增检查约束,使用量不能超过配额

⚠️ 执行前检查:

-- 检查生产环境是否有 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;

回滚方案:

-- 删除约束(如果需要)
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) 限制的问题

操作:

ALTER TABLE agent_billing_records ALTER COLUMN agent_type TYPE VARCHAR(100);

影响: 扩展字段长度,对现有数据无负面影响

测试建议:

-- 检查是否有被截断的数据
SELECT agent_type, LENGTH(agent_type) as len, COUNT(*) 
FROM agent_billing_records 
GROUP BY agent_type 
ORDER BY len DESC;

回滚方案:

-- 仅在确认所有值都 ≤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 的值

操作:

-- 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统计发生变化

⚠️ 执行前备份:

-- 备份历史数据
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;

验证:

-- 检查 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;

回滚方案:

-- 从备份表恢复
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 的问题

操作:

-- 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,提高数据完整性

执行前检查:

-- 检查有多少记录缺失 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调用计费(每次调用 = 一条新记录)

操作:

-- 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' 的记录

验证:

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;

回滚方案:

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: 备份生产数据库 🔴 必做

# 使用项目提供的备份脚本
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: 检查生产环境状态

# 检查数据库连接
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 scripts/sync_prod_database.sh

该脚本会:

  • ✅ 自动检查K8s集群连接
  • ✅ 显示当前数据库配置
  • ✅ 逐个执行迁移,每步都需要确认
  • ✅ 记录执行日志

步骤 4: 手动执行(如果需要更细粒度控制)

# 进入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: 验证结果

# 检查表结构
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: 重启服务(如果需要)

# 重启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
  • 这些主要是新增字段/表,对现有业务无影响

🔄 回滚计划

如果迁移后发现问题,按以下步骤回滚:

快速回滚(恢复备份)

# 停止服务
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 语句。


📊 预期影响评估

数据量影响

-- 评估受影响的记录数
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日志:

    kubectl logs -n taiji-ai-pad deployment/mcp-server --tail=100
    
  2. 数据库日志:

    kubectl logs -n taiji-ai-pad <postgres-pod> --tail=100
    
  3. 运行健康检查:

    python services/mcp-server/check_billing_health.py
    

📚 相关文档


📅 变更记录

日期 操作人 环境 迁移 结果 备注
2026-03-09~12 - 测试环境 017-024 ✅ 成功 初次执行
待填写 待填写 生产环境 017-024 待填写 待填写

最后更新: 2026-03-12
文档版本: v1.0