通过数据库迁移并排除日志表,成功将数据从3.3GB压缩至30MB,日均流量从20GB降至10GB以下。再将反向代理从Nginx切换至Caddy并启用zstd压缩,同时配置异地自动备份与健康检查,最终实现API网关性能优化与运维简化。
最近把自建的 AI API 网关从旧服务器迁移到新服务器,过程中碰到了一堆典型运维问题。数据库从 3.3GB 压缩到 30MB,日均流量从 20GB 降到 10GB 以下,反向袋里从 Nginx 换成了 Caddy,数据库还做了异地自动备份——这些数字背后,其实是一连串的优化和踩坑。这篇文章把整个过程和方案梳理了一下,希望能给有类似需求的朋友一点参考。

长期稳定更新的攒劲资源: >>>点此立即查看<<<
数据库一开始挺吓人的,3.3GB。但仔细一看,90%以上都是无价值的日志数据。用 pg_dump 把日志表排除了,核心业务数据才几十 MB,迁移效率直接拉满。
-- 找出哪些表占空间最大
SELECT table_name,
pg_size_pretty(pg_total_relation_size(table_name::text)) as total_size
FROM (SELECT tablename FROM pg_tables WHERE schemaname = 'public') t
ORDER BY pg_total_relation_size(table_name::text) DESC
LIMIT 10;
结果一目了然,前5张日志表就占了绝大部分:
| 表 | 大小 | 说明 |
|---|---|---|
| ops_system_logs | 2.2 GB | 系统操作日志 |
| usage_logs | 425 MB | API 调用记录 |
| usage_billing_dedup | 273 MB | 计费去重 |
| ops_error_logs | 260 MB | 错误日志 |
| scheduler_outbox | 63 MB | 调度队列 |
pg_dump -U user -d database -T ops_system_logs -T ops_error_logs -T usage_logs -T scheduler_outbox --no-owner --no-acl -Fc -f backup.dump
注意:如果应用代码里引用了这些表的索引或外键(比如 billing_usage_entries 引用了 usage_logs.id),那就得在目标库手动补上空表结构——只建表不插数据:
# 从源库导出这些表的 schema(不含数据) pg_dump -U user -d database -t ops_system_logs --schema-only > missing_tables.sql # 在目标库执行 psql -U user -d database < missing_tables.sql
| 指标 | 迁移前 | 迁移后 |
|---|---|---|
| 数据库大小 | 3.3 GB | 31 MB |
| 用户数 | 3,234 | 3,234(一致 ) |
| 迁移耗时 | — | 15 秒 |
原服务器用 Nginx 做反向袋里,切换到 Caddy 之后,好处是实打实的。
api.example.com {
# 反向袋里到后端
reverse_proxy localhost:8080 {
health_uri /health
health_interval 30s
health_timeout 10s
health_status 200
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
transport http {
keepalive 120s
keepalive_idle_conns 256
read_buffer 16KB
write_buffer 16KB
}
fail_duration 30s
max_fails 3
unhealthy_status 500 502 503 504
}
# 压缩配置(zstd + gzip 双支持)
encode {
zstd
gzip 6
minimum_length 256
match {
header Content-Type application/json*
header Content-Type application/ja vascript*
header Content-Type text/*
}
}
# 请求体限制
request_body {
max_size 50MB
}
}
拿一个典型的 JSON API 响应(35KB)来测试:
| 压缩方式 | 大小 | 压缩比 |
|---|---|---|
| 无压缩 | 35.7 KB | — |
| gzip | 15.7 KB | 56% |
| zstd | 15.9 KB | 55% |
实际跑下来,对文本类 API 响应,压缩率普遍在 50-70%,效果非常可观。
服务器流量消耗异常快,第一反应是怀疑被攻击或者有异常请求。
1. 检查 nginx/Caddy 日志中的实际流量
# 统计每日流量
sudo awk '{date=substr($4,2,11); bytes[date]+=$10}
END{for(d in bytes) printf "%s %.2f GBn", d, bytes[d]/1024/1024/1024}'
/var/log/caddy/api.log | sort
2. 按 URL 路径分析流量分布
sudo awk '{urls[$7]+=$10; count[$7]++}
END{for(u in urls) printf "%.2f GB (%d reqs) %sn", urls[u]/1024/1024/1024, count[u], u}'
/var/log/caddy/api.log | sort -rn | head -10
3. 找出单次响应异常的请求
sudo awk '$7 == "/responses" {if($10>max){max=$10; line=$0}}
END{printf "最大响应: %.1f MBn%sn", max/1024/1024, line}' access.log
/responses(AI 聊天 API)占流量的 80% 以上| 措施 | 效果 |
|---|---|
| 开启 zstd/gzip 压缩 | API 响应缩小 50-60% |
| 请求体限制 256MB → 50MB | 防止单次异常消耗 |
| 给高消耗用户加 RPM 限速 | 控制总请求量 |
每天凌晨自动备份数据库,排除日志表,保留 7 天。备份同时传输到远程服务器做异地容灾,安全系数提升不少。
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/data/backups"
REMOTE_SERVER="user@backup.example.com"
RETENTION_DAYS=7
# 排除的日志表
EXCLUDE_TABLES=(
ops_system_logs
ops_error_logs
usage_logs
scheduler_outbox
usage_billing_dedup
)
mkdir -p "$BACKUP_DIR"
BACKUP_FILE="db_backup_$(date +%Y%m%d_%H%M%S).dump"
# 构建排除参数
EXCLUDE_ARGS=""
for table in "${EXCLUDE_TABLES[@]}"; do
EXCLUDE_ARGS="$EXCLUDE_ARGS -T $table"
done
# 备份
docker exec postgres pg_dump -U user -d database
$EXCLUDE_ARGS --no-owner --no-acl -Fc > "$BACKUP_PATH"
# 验证备份完整性
docker run --rm postgres:18-alpine pg_restore -l "$BACKUP_FILE" || exit 1
# 传输到远程
scp "$BACKUP_PATH" "${REMOTE_SERVER}:${BACKUP_DIR}/"
# 清理过期备份
find "$BACKUP_DIR" -name "*.dump" -type f -mtime +$RETENTION_DAYS -delete
设置定时任务:
echo "0 5 * * * root /opt/scripts/daily_backup.sh" > /etc/cron.d/db-backup
切换到 Caddy 后,有时会遇到 503 错误。这个错误挺让人头疼的,但原因其实很简单——Caddy 的健康检查发现后端不可用后,会把后端标记为不可用一段时间(fail_duration 30s)。
解决方法:
# 重启后端后需要重载 Caddy caddy reload --config /etc/caddy/Caddyfile
或者调整健康检查配置,降低判定阈值:
reverse_proxy localhost:8080 {
health_uri /health
health_interval 10s
health_timeout 5s
fail_duration 10s
max_fails 1
}
请求体超出限制时返回 413。排查方法如下:
# 查看 nginx/Caddy 错误日志 grep "413" /var/log/caddy/*.log # 确认当前限制值 grep max_size /etc/caddy/Caddyfile
如果经过 Cloudflare,还得注意它免费版限制 100MB 的最大请求体,超过会被 Cloudflare 直接拦截。
这次迁移下来,几点经验值得记住:
pg_restore -l 检查备份完整性,否则等于没备份侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述