Skip to content

数据、缓存与一致性

Sub2API 同时依赖 PostgreSQL、Redis 和进程内缓存。理解每类数据的归属,才能正确备份、扩容和排障。

PostgreSQL:事实来源

长期业务数据以 PostgreSQL 为准:

数据域代表表
身份与用户usersauth_identitiespending_auth_sessions
网关资源api_keysaccountsgroupsaccount_groupsproxies
权益user_subscriptionsuser_platform_quotasredeem_codes
支付payment_orderspayment_provider_instancespayment_audit_logs
用量usage_logsusage_billing_dedup、清理任务
运维error logs、metrics、alert events、job heartbeats
批量图片job、item、event 表

管理员在页面中修改账号、用户和分组,本质上是在修改这些表,再触发缓存失效或快照更新。

Redis:运行时协调

常见 Redis 数据可以分为五类:

鉴权与业务缓存

  • API Key 快照。
  • 用户余额和订阅摘要。
  • 用户平台配额。
  • 分组倍率和模型列表。

这类缓存丢失后通常可以从 PostgreSQL 重建,但会造成数据库回源压力。

并发与限流

  • 用户、API Key、账号并发 ZSET。
  • 等待队列计数。
  • RPM 与费用窗口。
  • 模型、账号和上游错误冷却状态。

它们决定请求是否能执行。Redis 故障时不能简单假设“只是慢一点”。

会话与连接提示

  • 粘性会话账号绑定。
  • Previous Response ID 绑定。
  • 临时账号选择和 session limit。

这类数据通常带 TTL,丢失会降低会话连续性,但不应破坏永久业务数据。

分布式锁

  • OAuth token 刷新锁。
  • 周期任务 leader lock。
  • 幂等与临界区锁。

锁 value 包含 owner,释放时采用 compare-and-delete,防止过期持有者删除新持有者的锁。

队列

  • 批量图片 ready/delayed/active 队列。
  • inflight 与 job lock。
  • 用户消息串行队列。

队列数据是否可丢取决于功能。启用异步批量任务时,应为 Redis 配置持久化和恢复策略。

进程内状态

  • L1 鉴权与订阅缓存。
  • HTTP client 和连接池。
  • OpenAI WebSocket pool。
  • worker pool 队列与指标。
  • 本地配置快照与去重日志状态。

进程重启会丢失这些状态。正常设计应允许重建,但正在执行的流式请求和内存队列任务会中断。

API Key 多级缓存

典型读取链:

text
request key

   ├─ L1 hit ──────────────▶ auth snapshot

   ├─ Redis L2 hit ────────▶ populate L1

   └─ PostgreSQL ──────────▶ populate L2 + L1

配置中可以调整 L1 大小、L1/L2 TTL、negative cache TTL、jitter 和 singleflight。负缓存可以减少无效 Key 攻击造成的数据库查询,但也意味着刚创建的 Key 必须正确触发缓存失效。

调度快照

高并发时每个请求都从 PostgreSQL查询账号与分组关系成本很高。调度器维护账号候选快照,并通过:

  • 管理写路径触发更新。
  • scheduler outbox 传播变化。
  • 周期性全量重建。
  • 受控数据库 fallback。

来保持快照可用。

重要监控包括 outbox lag、backlog、快照重建失败和 DB fallback QPS。调度缓存异常会表现为“后台账号已修改,但网关仍使用旧状态”。

用量写入

请求结束后,RecordUsage 可能更新:

  • usage log。
  • 用户余额或订阅窗口。
  • API Key quota 与费用窗口。
  • 用户平台配额。
  • 账号成本和使用统计。
  • 仪表盘缓存与聚合输入。

项目使用有界 worker pool,默认配置以同步 fallback 保护计费任务。运维不能只看 HTTP 成功率,还要看用量任务失败、丢弃和数据库写延迟。

幂等与去重

系统中存在两类容易混淆的幂等:

  • 管理/支付操作幂等:避免重复请求创建或执行相同操作。
  • usage billing dedup:避免同一请求或同一 turn 重复扣费。

它们有不同 key、TTL 和失败状态。清理过早可能导致重复执行,保留过久会增加数据库体积。

缓存失效而不是缓存更新

很多写路径选择删除缓存或递增版本,让下一次读取重新构建,而不是尝试在多个缓存结构中同步修改。这降低写路径复杂度,但会造成短暂 cold miss。

发布或批量修改后出现短时间数据库 QPS 上升,可能是缓存集中失效,而不一定是流量增长。

Redis 故障时会发生什么

不同组件的策略不同:

  • 某些只读缓存可以回源 PostgreSQL。
  • 并发、限流和调度可能 fail closed 或返回服务不可用。
  • leader lock 获取失败时,部分任务回退 PostgreSQL advisory lock。
  • 粘性会话可能退化为普通调度。
  • 账号 OAuth 刷新可能因无法加锁而等待或失败。

不能用一句“Redis 有降级”概括全部行为。生产演练应分别验证鉴权、普通请求、OAuth 刷新、后台任务和批量任务。

备份范围

数据必须备份说明
PostgreSQL所有长期业务事实
.env / config固定密钥与运行配置
Redis视功能批量任务/队列启用时重要性更高
data/配置、日志、定价、备份和自定义文件
进程内缓存重启自动重建

备份加密和恢复演练见升级、备份与迁移

本文档用于帮助你理解、部署和运维 Sub2API。使用前请确认上游服务条款与当地法律要求。