跳转到内容
GitHub

主题 / 后端与数据

17 数据库与存储

关系库的建模与命名、表结构迁移原则、事务与并发控制(隔离级别、乐观锁、行锁领取、租约)、outbox 表与幂等表、连接池与查询性能、备份与恢复演练、对象存储和向量存储怎么做。

草稿复核于 2026-11-14 · 下次复核 2027-05-1440 条规则 · 硬约束 29 条在 GitHub 上修改

一页结论

  1. 新项目默认 PostgreSQL。 客户已有 MySQL 或指定国产库就沿用;「兼容 PostgreSQL / MySQL」的库要单独跑方言测试,不假设等价。
  2. 约束放进数据库。 主键、非空、唯一、外键、检查约束在库里表达;时间点用带时区类型,金额不用浮点。
  3. 表结构只走版本化迁移。 先加后删(扩展-收缩),让新旧两版应用能同时跑;回滚靠向前修复,不靠 down 脚本。迁移在同款同版本的库上、用真实规模的数据实测。
  4. DDL 也会锁表。 先设锁等待超时;大表建索引用并发方式;加约束先不校验存量、再单独校验;回填分批、可续跑。
  5. 事务短而明确。 事务边界在业务用例层划,事务里不调外部服务;用更高隔离级别时要能整体重试序列化失败。
  6. 并发写有保护。 乐观锁检查影响行数;多个 worker 领取任务用 SKIP LOCKED 加租约和 fencing token;能用现成队列库就别手写(见 18)。
  7. outbox 与业务同事务。 投递至少一次,消费者按事件 ID 幂等;幂等表的结果和业务变更一起提交。
  8. 主从不是备份。 需要时间点恢复就用基础备份加连续 WAL 归档;只有恢复演练成功过的备份才算数。
  9. 存储先用简单的。 向量先用 pgvector,按压测结果再换专用库;对象存储只依赖 S3 的常用子集,新项目不选已归档的 MinIO 社区版。

规则

级别
检查方式
显示 40 条
DB-001硬约束检查:自动

每张有业务身份的表必须有主键;纯关联表可以用组合主键

怎么检查:CI 查询系统目录,列出没有主键的表,与登记的例外清单比对
为什么、来源
为什么:没有主键的行无法可靠地更新、引用和去重,逻辑复制等功能也会受限。
DB-002软指导检查:半自动

表、列、约束、索引用小写加下划线命名;可变的业务编号不当技术主键,另建唯一约束

怎么检查:用迁移 lint 或自写脚本检查命名;评审时看主键是否用了订单号这类业务编号
为什么、来源
为什么:统一命名在多种数据库之间可移植;业务编号的规则会变,拿它当主键改起来牵连所有外键。
DB-003软指导检查:半自动

对外暴露或跨系统生成的 ID 用 UUIDv7,单库内部的高写入表可用自增 bigint;任何 ID 都不当作访问凭证

怎么检查:评审表结构时看主键类型;搜索代码里是否有「知道 ID 就能访问」的接口
为什么、来源
为什么:UUIDv7 按时间大致有序,索引局部性比随机 UUID 好,又能在多处独立生成;RFC 9562 明确 UUID 不能当安全凭证,而 UUIDv7 还能推出创建时间。
  • 数据库没有内置 UUIDv7 生成函数时,在应用里用经过测试的库生成。
  • 不想暴露创建时间的场景(如对象存储 key)用随机值。
  • ULID 只在已有 ULID 生态时沿用,不作默认。
DB-004硬约束检查:半自动

表示某个时刻的列用带时区的时间类型(PostgreSQL 用 timestamptz);需要本地业务时间的,另存 IANA 时区名

怎么检查:迁移 lint 或脚本列出不带时区的时间列,评审确认是否属于「本地日期时间」这类例外
为什么、来源
为什么:不带时区的时刻在跨时区、夏令时和服务器改时区时会悄悄算错。

「每天早上 9 点」这类本地时间规则存本地时间加时区名,不要提前换算成 UTC。

DB-005硬约束检查:半自动

金额和要求精确的数值用 numeric/decimal 或约定单位的整数(如「分」),不用浮点类型

怎么检查:迁移 lint 或脚本列出 real、double precision、float 类型的列,评审确认不是金额或计量值
为什么、来源
为什么:浮点数是二进制近似值,加总和比较会出现误差;PostgreSQL 官方建议金额用 numeric。
DB-006软指导检查:半自动

非空、唯一、外键、检查约束在数据库里表达;要去掉外键等约束,写明理由并登记

怎么检查:评审迁移时看新表是否声明了约束;去掉约束的迁移要求 PR 里附理由
为什么、来源
为什么:只靠应用代码守一致性,任何一条绕过它的写入路径(脚本、后台任务、另一个服务)都会写进脏数据。
  • 状态值少且稳定时用 CHECK 约束或查找表;PostgreSQL 原生枚举删除或重排值很麻烦。
  • JSONB 适合稀疏、变化快的扩展字段;经常用来筛选或关联的稳定属性独立成列。写明 JSON 字段的结构由谁负责、怎么演进。
DB-007软指导检查:人工

先定删除语义:默认真删除;确有恢复或留存需要才用软删除,并用部分唯一索引处理唯一约束

怎么检查:设计评审时确认每类实体的删除方式,以及对象存储、搜索、向量副本、下游事件的清理责任
为什么、来源
为什么:无期限的软删除会让个人信息删不干净,唯一约束也会被已删除的行占住。

软删除表的唯一约束只约束未删除的行,例如: CREATE UNIQUE INDEX ... ON documents (tenant_id, slug) WHERE deleted_at IS NULL。 删主表一行不等于整条链路都删了。法规上的删除义务见 27。

DB-008硬约束检查:半自动对外提供服务的后端

多个租户共用一张表时,唯一约束、外键和常用索引都包含租户列

怎么检查:脚本列出带租户列的表中不含租户列的唯一约束和外键;评审确认
为什么、来源
为什么:只按实体 ID 建外键,可以把 A 租户的记录挂到 B 租户的对象上;唯一约束不带租户会让不同租户互相冲突。

租户隔离模型(共享表、独立 schema、独立库)怎么选见 19。

DB-009硬约束检查:自动对外提供服务的后端

使用 PostgreSQL 行级安全(RLS)时,应用连接用的角色不得是表所有者、超级用户或带 BYPASSRLS 属性,并对表开启 FORCE ROW LEVEL SECURITY

怎么检查:CI 查询 pg_roles 和 pg_class,检查应用角色的属性和表的 relforcerowsecurity;再用跨租户负例测试验证
为什么、来源
为什么:超级用户和 BYPASSRLS 角色总能绕过 RLS,表所有者默认也绕过;不检查角色,RLS 形同虚设。
  • RLS 是第二道防线,不替代应用层授权。
  • 租户上下文用事务级设置(如 set_config(..., true)),不用会话级设置,避免连接复用时残留(见 DB-032)。
DB-010软指导检查:半自动

分区只在有明确的生命周期、查询或数据量需求时使用;用分区就要自动预建未来分区并有清理流程

怎么检查:监控默认分区的行数增长;评审分区方案时看清理和归档步骤
为什么、来源
为什么:分区不会自动加速,还会限制唯一约束(必须包含分区键);忘了建新分区,写入会落进默认分区或直接失败。

删除或分离旧分区前,先确认已归档或已备份。

DB-011硬约束检查:半自动

生产库的表结构只通过版本化迁移变更;迁移作为单独的发布步骤用专用角色执行,不在应用启动时自动同步或每个实例都跑

怎么检查:搜索生产代码中 ORM 的自动同步或建表调用;检查部署流程里迁移是单独的步骤;业务角色没有 DDL 权限
为什么、来源
为什么:自动同步不留记录、不能评审,多个实例同时跑迁移还会互相抢锁。

用哪个迁移工具按语言选,见 10–15。ORM 自动生成的迁移必须人工审查生成的 SQL。

另见:10-15
DB-012硬约束检查:半自动对外提供服务的后端

不兼容的表结构变更按扩展-收缩分步做:先新增、再回填和切换、最后删除旧结构;每一步都要让当前版本和上一版本的应用能同时运行

怎么检查:CI 用上一版本的应用代码跑新迁移后的库做冒烟测试;评审时检查删列、改名、改类型是否拆成了多次发布
为什么、来源
为什么:滚动发布时新旧版本的应用会同时访问数据库,一次性改名或删列会让旧版本立刻出错。

典型步骤:新增可空列或新表 → 分批回填 → 校验(空值、计数、抽样比对)→ 应用切换到新结构 → 至少过一个发布周期后删除旧结构。 删除旧结构的迁移要单独评审。

DB-013硬约束检查:人工

回滚以向前修复为主;删除数据的迁移标明不可逆,不靠 down 脚本声称可以恢复

怎么检查:PR 模板要求写明迁移是否可逆、出问题时的应用回退方式和数据修复方案;评审确认
为什么、来源
为什么:down 脚本恢复不了已经删掉的列和数据;真出事时,发一个修复迁移往往比回退更快更安全。

迁移前确认备份和时间点恢复可用(见 DB-033)。

DB-014硬约束检查:半自动

迁移在与生产同款、同大版本的数据库上验证:既从空库跑,也在上一版本的脱敏数据快照上跑

怎么检查:CI 起与生产同版本的数据库容器跑迁移;发布前在脱敏快照上演练并记录耗时
为什么、来源
为什么:空库上能跑通的迁移,遇到真实数据(重复值、空值、大表)常常失败;不同数据库甚至不同大版本的 DDL 行为也不一样。
DB-015硬约束检查:自动

PostgreSQL 迁移按安全写法:DDL 前设 lock_timeout;大表建索引用 CREATE INDEX CONCURRENTLY 且不放在事务里,完成后检查索引有效;加约束先 NOT VALID 再单独 VALIDATE

怎么检查:CI 对迁移文件跑 Squawk;迁移执行后查询 pg_index.indisvalid
为什么、来源
为什么:等锁的 DDL 会把后面所有读写都堵住;普通建索引会阻塞写入;直接加约束会在持锁期间扫全表。
  • 等不到锁时让迁移快速失败(错误码 55P03)再重试,比把业务堵住好。
  • CREATE INDEX CONCURRENTLY 失败会留下无效索引,要删掉重建。
  • 加 NOT NULL:先加 CHECK (col IS NOT NULL) NOT VALID,再 VALIDATE CONSTRAINT,确认后再 SET NOT NULL。
DB-016软指导检查:人工

大表变更的 PR 写明锁级别、是否重写全表、预计耗时和执行窗口;lint 管不到的风险由人审查

怎么检查:PR 模板固定这些栏目;评审者对照表大小和流量确认
为什么、来源
为什么:静态检查不知道生产表有多大、流量多高,同一条 ALTER 在小表上瞬间完成,在大表上可能停服。
DB-017硬约束检查:半自动

数据回填分批、幂等、可暂停续跑,不和 DDL 放在同一个事务里

怎么检查:集成测试中途中断回填再重启,检查结果正确且不重复处理
为什么、来源
为什么:一个大事务回填会长时间持锁、产生大量日志,失败了只能从头再来。

按主键游标分批,每批短事务;记录游标位置、处理量和失败量;设语句超时并限速。

DB-018硬约束检查:半自动

MySQL 迁移不套用 PostgreSQL 的事务 DDL 假设;在线变更显式指定 ALGORITHM 和 LOCK,并在同版本实例上验证

怎么检查:迁移 lint 检查 ALTER 语句是否写了 ALGORITHM/LOCK;在同版本 MySQL 上演练,包括中途中断
为什么、来源
为什么:MySQL 的 DDL 会隐式提交当前事务,失败时不能整体回滚;各种 ALTER 能否在线执行随版本和表结构而变。

原生在线 DDL 不满足时再考虑 gh-ost 或 pt-online-schema-change,并先审查外键、触发器和复制的限制。

DB-019硬约束检查:半自动私有化或断网部署

「兼容 PostgreSQL」或「兼容 MySQL」的数据库(如 openGauss、金仓、TiDB、OceanBase)单独跑方言测试,不假设与原版等价

怎么检查:CI 或验收环境对每种目标库跑同一套契约测试:类型、时区、唯一与空值、upsert、锁等待、SKIP LOCKED、隔离级别、DDL、错误码
为什么、来源
为什么:兼容库在语法、函数、扩展、锁和 DDL 上都有差异,例如 TiDB 官方列出了不支持的功能;不测就承诺兼容,到客户现场才暴露。

每个迁移文件写明适用的方言;没在目标库上测过的 SQL 不复制过去用。

DB-020硬约束检查:半自动对外提供服务的后端

事务边界在业务用例层划定;事务内不调用大模型、邮件、远程 HTTP 等外部服务

怎么检查:评审事务代码块内的调用;监控长事务和「事务中空闲」的连接
为什么、来源
为什么:外部调用慢或挂住时,事务会长时间持锁、占连接,拖垮整个库;外部副作用也无法随事务回滚。
  • 需要外部副作用的,先在事务里写 outbox 或任务记录,提交后再执行(见 DB-027)。
  • PostgreSQL 可以设 idle_in_transaction_session_timeout 兜底。
  • 各语言的事务写法(开启、回滚、检查提交错误)见 10–15。
另见:10-15、01 架构与设计原则
DB-021软指导检查:人工对外提供服务的后端

按用例显式选隔离级别;默认的读已提交级别下「先读后写」有竞态的,用行锁、乐观锁或更高隔离级别保护

怎么检查:评审「先查再改」的代码路径,看用了哪种保护;用并发测试复现竞态
为什么、来源
为什么:PostgreSQL 默认是读已提交,两个事务读到同一个旧值后各自写入,就会丢掉一方的修改或打破业务规则。
  • 单行更新:用 SELECT ... FOR UPDATE 或乐观锁(DB-023)。
  • 跨多行的不变量:考虑可串行化级别,并按 DB-022 重试。
DB-022硬约束检查:半自动对外提供服务的后端

使用可重复读或可串行化级别时,序列化失败(40001)和死锁(40P01)要整体重试整个事务,有次数上限和退避;唯一冲突(23505)按业务处理,不盲目重试

怎么检查:检查事务封装里对 SQLSTATE 的分类;并发测试制造序列化失败,确认会重试且最终结果正确
参考值:重试次数和退避时长无官方统一值;参考 3 次左右、带随机抖动的指数退避,并受整个请求的超时预算约束。
为什么、来源
为什么:PostgreSQL 官方说明这两个级别下应用必须准备好重试序列化失败;只重试失败的那条语句是错的,要从头重做整个事务。
  • 只重试可以安全重复的整个数据库用例;事务里不能有已经发生的外部副作用。
  • 唯一冲突通常意味着「已存在」,转成业务错误或走幂等逻辑。
DB-023硬约束检查:半自动对外提供服务的后端

乐观锁用版本列:更新时带上期望版本并递增,检查影响行数;冲突时返回业务错误,不自动覆盖

怎么检查:并发更新测试:两次同时修改同一行,一方必须收到冲突错误
为什么、来源
为什么:不检查影响行数,版本冲突会被当作成功,后写的人悄悄覆盖先写的人。

UPDATE t SET ..., version = version + 1 WHERE id = $1 AND version = $2,影响 0 行时再区分「不存在」还是「版本冲突」。 编辑类冲突通常应提示用户重新加载,只有允许丢弃旧值的场景才自动重试。冲突在接口上怎么表达见 16。

DB-024硬约束检查:半自动

SKIP LOCKED 只用于多个消费者领取队列式表里的任务,不用于需要一致视图的普通查询

怎么检查:搜索代码中的 SKIP LOCKED,确认都在任务领取路径上
为什么、来源
为什么:PostgreSQL 官方说明跳过锁定的行会得到不一致的数据视图,不适合一般用途,只适合避免多个消费者抢同一行。

需要重试、优先级、定时、监控的,优先用现成的数据库队列库,不手写(见 18)。

DB-025硬约束检查:半自动对外提供服务的后端

多个 worker 领取任务时用租约:用数据库时钟记录到期时间,到期可被回收;每次领取递增一个 fencing token,完成、续约和写结果时都校验它

怎么检查:故障测试:让持有租约的 worker 暂停到租约过期,另一个 worker 接手后,原 worker 恢复时的写入必须被拒绝
为什么、来源
为什么:只有超时没有 fencing token,暂停后醒来的旧 worker 会覆盖新 worker 的结果。
  • 领取:FOR UPDATE SKIP LOCKED 选出待办或租约已过期的行,更新状态、持有者、到期时间,并把 token 加 1。
  • 写入:WHERE id = $1 AND lease_token = $2,影响 0 行就视为租约已丢,停止处理。
  • 用数据库的 now() 而不是各 worker 的本地时钟;租约时长要明显长于心跳间隔。
  • 外部副作用仍要靠业务幂等。分布式锁服务见 18。
DB-026硬约束检查:半自动对外提供服务的后端

业务写入和待发事件需要原子时,outbox 记录与业务变更在同一个事务里提交

怎么检查:故障注入测试:业务事务回滚时 outbox 不能有记录;提交后、投递前中继崩溃,重启后事件仍会发出
为什么、来源
为什么:先提交再发消息、或先发消息再提交,中间崩溃就会丢事件或发出不存在的事件。

原则见 ARC-015。能用数据库变更捕获(CDC)的,也可以代替轮询 outbox 表。

DB-027硬约束检查:半自动对外提供服务的后端

outbox 中继按至少一次投递设计:带领取租约、重试次数上限、失败转人工处理、已发送记录定期清理;消费者按事件 ID 幂等

怎么检查:测试投递后、标记已发送前中继崩溃的情况,确认消费者收到重复事件也只处理一次
为什么、来源
为什么:中继可能在发出后、记账前崩溃,重复投递无法避免;只建一张表没有领取和重试协议,不算可用的 outbox。
  • 最少字段:事件 ID、聚合类型与 ID、事件类型、载荷、发生时间、状态、尝试次数、下次尝试时间、发送时间。
  • 同一聚合需要顺序时,显式加序号,不靠 UUIDv7 或提交时间推断。
  • 中继到消息组件、消费者框架见 18;事件格式和版本见 16。
DB-028硬约束检查:半自动对外提供服务的后端

幂等表以「租户 + 操作 + 幂等键」做唯一约束并保存请求指纹;处理结果与业务变更在同一个事务里提交

怎么检查:并发测试:同一个键同时发两次只执行一次;同一个键换了请求内容,按 16 的约定返回冲突
为什么、来源
为什么:只靠先查后插会在并发下重复执行;不存指纹就分不清是重试还是误用了同一个键。
  • 用 INSERT ... ON CONFLICT 抢占;不支持该语法的兼容库,用插入加捕获唯一冲突再查询,并先测过其并发语义。
  • 请求指纹按规范化后的内容计算(字段顺序不影响结果)。
  • 记录过期就允许重放;资金类操作要用长期有效的业务操作 ID,不能只靠短期键。
  • 状态码和响应语义(不同请求 422、处理中 409)以 16 为准。
DB-029硬约束检查:半自动对外提供服务的后端

连接预算:各服务「连接池上限 × 实例数」加上管理和迁移连接,总和不超过数据库允许的连接数

怎么检查:部署配置检查脚本按最大扩容实例数计算总和并与数据库上限比较;压测时观察连接数
为什么、来源
为什么:自动扩容后连接数成倍增加,超过上限就会让所有新请求失败。

每个进程只建一个连接池(各语言写法见 10–15)。连接数参数的具体值按库的规格定,不给统一数字。

另见:10-15
DB-030软指导检查:半自动对外提供服务的后端

启用 PgBouncer 这类事务级连接池前,先测会话状态、预编译语句、临时表、LISTEN/NOTIFY 和 RLS 租户上下文

怎么检查:连接池集成测试:两个请求交替复用同一连接,检查设置和租户上下文不会串
为什么、来源
为什么:事务级连接池下,同一个客户端的下一条事务可能落到另一个服务端连接上,会话级的设置会丢失或串给别人。

租户上下文和会话变量只用事务级设置;预编译语句的支持取决于连接池配置和驱动,要实测。

DB-031软指导检查:半自动

新增关键查询或改索引时,附上在有代表性数据量上的执行计划;每个索引对应具体查询,不给每列都建索引

怎么检查:PR 附 EXPLAIN (ANALYZE, BUFFERS) 结果;定期用 pg_stat_statements 找慢查询,查未使用的索引
为什么、来源
为什么:小数据量上的计划在大表上会完全不同;多余的索引拖慢写入、占磁盘。
  • 先测量再优化:查询写法、N+1、返回列、索引和统计信息都排查过,再考虑加缓存或读副本。
  • 大数据集分页用基于游标的 keyset 方式并配合索引,不对外提供无上限的 OFFSET;分页接口约定见 16。
  • 在生产上跑 ANALYZE 会真的执行语句,注意开销和脱敏。
DB-032硬约束检查:半自动对外提供服务的后端

有读副本时,为每类查询写明一致性要求;读自己刚写的数据、授权判断、事务内的读一律走主库

怎么检查:评审数据访问层的路由规则;一致性测试:写入后立刻读,不能读到旧值
为什么、来源
为什么:副本有复制延迟,把所有读都路由到副本会让用户看不到刚提交的修改,授权判断也可能用到旧权限。
DB-033硬约束检查:人工对外提供服务的后端私有化或断网部署

生产数据有书面的恢复目标(RPO/RTO)、保留期、负责人和恢复手册;备份范围覆盖数据库、对象存储、配置和密钥恢复材料

怎么检查:发布前检查恢复手册是否存在并列明这些项;演练时按手册执行
为什么、来源
为什么:没有书面目标就没法判断备份方案够不够;只备份数据库,对象文件和解密密钥丢了照样恢复不了业务。

RPO 是最多能丢多少时间的数据,RTO 是多久内要恢复。数值按业务定,不给统一值。

DB-034硬约束检查:半自动

主从复制和存储快照不是备份;需要时间点恢复的,用基础备份加连续 WAL 归档(MySQL 用基础备份加 binlog),不能只靠 pg_dump

怎么检查:检查备份工具配置包含 WAL 归档;恢复演练中恢复到指定时间点
为什么、来源
为什么:误删、逻辑错误和勒索软件会随复制同步到备库;pg_dump 是逻辑快照,恢复不到任意时间点。
DB-035硬约束检查:半自动对外提供服务的后端私有化或断网部署

备份加密,密钥与备份分开保管;至少一份放在另一个故障域且生产账号不能删改;备份失败、WAL 归档落后要告警

怎么检查:审计备份存储的权限和位置;告警规则覆盖备份任务失败、归档延迟、空间不足
为什么、来源
为什么:备份和生产在同一个账号、同一处机房里,一次误操作或入侵就能一起毁掉;备份悄悄失败几周没人发现很常见。

告警和值班流程见 26,密钥管理见 25。

DB-036硬约束检查:人工对外提供服务的后端私有化或断网部署

定期在隔离环境里做真实恢复演练,校验数据完整、应用可用,并记录实际恢复时间和数据损失

怎么检查:检查演练记录:备份版本、选定的恢复时间点、开始和结束时间、校验 SQL 及结果、实际数据损失、负责人
参考值:参考值:普通生产每季度至少一次抽样恢复、每半年至少一次完整恢复;关键系统更频繁;数据库大版本升级、更换备份工具或存储后加做一次。无官方统一要求,按恢复目标和变更频率调整。
为什么、来源
为什么:备份任务显示成功不等于能恢复:WAL 缺段、密钥丢失、恢复脚本过时,都要真的恢复一次才发现。

本条是恢复演练频率的唯一出处,其他主题引用这里。

DB-037硬约束检查:半自动

跟上数据库当前的小版本,主版本在社区支持期内;升级数据库或备份工具前读发布说明,执行其中要求的额外步骤

怎么检查:脚本比对运行版本与官方支持列表;升级清单里有「读发布说明」一项并记录结论
为什么、来源
为什么:PostgreSQL 每个主版本支持 5 年、至少每 3 个月发一次小版本修复,官方认为不升小版本比升级风险更大;部分小版本要求重建索引或重做全量备份。

规则不写具体版本号;版本基线单独记账,按官方支持页更新。

DB-038软指导检查:半自动对外提供服务的后端私有化或断网部署

应用通过一层适配只使用 S3 的常用子集,并在选定的存储产品上跑兼容测试;数据库元数据和对象之间用状态字段加定时对账

怎么检查:对选定后端跑上传、分片上传、删除、列举、预签名的契约测试;对账任务报告孤儿对象和缺失对象
为什么、来源
为什么:「S3 兼容」的产品在分片、版本、对象锁等行为上差别很大;数据库和对象存储之间没有分布式事务,失败后两边会不一致。

元数据表记录状态、大小、校验和、检测出的类型、所属租户;未完成的分片上传要有清理规则。上传安全检查和预签名链接的要求见 25。

DB-039硬约束检查:半自动对外提供服务的后端

对象 key 由服务端生成,不可猜测,带租户前缀;不用用户原始文件名或个人信息做 key

怎么检查:上传集成测试检查生成的 key;评审代码中 key 的拼接方式
为什么、来源
为什么:用原文件名会冲突、可被猜到,还会把个人信息写进日志和存储路径。

原始文件名存进元数据表。

DB-040软指导检查:人工调用大模型的应用

向量存储先用 pgvector;按自己的数据和过滤条件压测召回率、延迟和维护成本,不达标再换专用向量库

怎么检查:压测记录:召回率(对比精确检索)、p95 延迟、带过滤的返回条数、建索引时间、备份恢复时间
为什么、来源
为什么:向量和业务数据在同一个库里能共用事务、备份和权限;不按负载测就上独立集群,多了一套要同步和运维的系统。
  • 近似索引是先查索引再过滤,过滤条件严时返回条数会不足;pgvector 提供迭代扫描、部分索引和分区来缓解,要实测。
  • 多个租户共用一个近似索引会互相影响召回和速度,可按租户分区或分表,但不要无限增加分区。
  • 不写「多少条以上必须换专用库」这类阈值。切块、检索和重排策略见 24。

选型

类别默认备选(何时用)不推荐
关系数据库PostgreSQL,使用社区支持期内的主版本和该主版本的最新小版本 · PostgreSQL License;MySQL 社区版为 GPLv2,另有商业许可,口径见 27
新项目
MySQL(LTS 发布线)(客户指定、团队已有 MySQL 运维和备份体系,或已有大量存量)
客户已验收的国产库(openGauss、金仓、达梦、OceanBase、TiDB 等)(信创或客户指定;先拿到产品、版本和测试环境并通过方言测试(DB-019))
已停止支持的主版本:不再有安全修复
未正式发布的主版本:不用于生产
PostgreSQL 迁移静态检查Squawk · MIT 或 Apache-2.0 双许可——
PostgreSQL 连接池应用或驱动自带的连接池
PgBouncer(实例多、连接数膨胀时;开事务池模式前按 DB-030 测试)
—
PostgreSQL 备份与时间点恢复pgBackRest · pgBackRest 为 MIT;WAL-G 为 Apache-2.0(其中 lzo 支持部分为 GPL-3.0+);Barman 为 GPL-3.0
WAL-G(主要备份到对象存储、已在用 WAL-G 的团队)
Barman(想要集中管理多台数据库的备份服务器;用 CloudNativePG 时走其 Barman Cloud 插件)
只用 pg_dump:恢复不到任意时间点(DB-034)
只靠备库或存储快照:误删会被同步,快照不一定一致
MySQL 备份与在线表结构变更Percona XtraBackup 加 binlog 归档;表结构变更先用原生在线 DDL
gh-ost 或 pt-online-schema-change(原生在线 DDL 不满足时;先审外键、触发器和复制的限制)
认为备份工具自动支持新的 MySQL 主版本:要按工具的兼容表核实并做恢复演练
PostgreSQL 高可用按恢复目标分级:单机加异地备份 → 主备人工切换 → Patroni 自动切换 → Kubernetes 上用 CloudNativePG
先定 RPO/RTO(DB-033)再选级别
—
小规模单机系统直接上三节点自动切换集群:运维复杂度超过收益,且高可用不代替备份
只为跑数据库而引入 Kubernetes:多一套平台要运维
对象存储单机或附件少时用本地文件系统加元数据表;多机时用 SeaweedFS · SeaweedFS 为 Apache-2.0;Garage 为 AGPLv3;MinIO 为 AGPLv3。分发和对外服务的义务口径见 27
Garage(轻量、多地点自托管;先做 S3 功能 PoC,注意 AGPLv3)
Ceph RGW(已有 Ceph 运维团队)
MinIO 社区版(新项目):官方仓库已于 2026-04-25 归档、不再维护,社区版不再提供预编译包;已有部署要规划迁移
自己写 S3 兼容服务:签名、分片、版本等协议细节难以做全
向量存储pgvector · pgvector 为 PostgreSQL License
Qdrant(压测后 pgvector 达不到要求,需要独立资源和带过滤的近似检索优化)
Milvus(规模很大且团队能运维分布式系统)
没有瓶颈就先上独立向量集群:多一套系统要同步、备份和运维
关系数据库
PostgreSQL,使用社区支持期内的主版本和该主版本的最新小版本 · PostgreSQL License;MySQL 社区版为 GPLv2,另有商业许可,口径见 27
备选:MySQL(LTS 发布线)(客户指定、团队已有 MySQL 运维和备份体系,或已有大量存量);客户已验收的国产库(openGauss、金仓、达梦、OceanBase、TiDB 等)(信创或客户指定;先拿到产品、版本和测试环境并通过方言测试(DB-019))
不推荐:已停止支持的主版本;未正式发布的主版本
PostgreSQL 迁移静态检查
Squawk · MIT 或 Apache-2.0 双许可
PostgreSQL 连接池
应用或驱动自带的连接池
备选:PgBouncer(实例多、连接数膨胀时;开事务池模式前按 DB-030 测试)
PostgreSQL 备份与时间点恢复
pgBackRest · pgBackRest 为 MIT;WAL-G 为 Apache-2.0(其中 lzo 支持部分为 GPL-3.0+);Barman 为 GPL-3.0
备选:WAL-G(主要备份到对象存储、已在用 WAL-G 的团队);Barman(想要集中管理多台数据库的备份服务器;用 CloudNativePG 时走其 Barman Cloud 插件)
不推荐:只用 pg_dump;只靠备库或存储快照
MySQL 备份与在线表结构变更
Percona XtraBackup 加 binlog 归档;表结构变更先用原生在线 DDL
备选:gh-ost 或 pt-online-schema-change(原生在线 DDL 不满足时;先审外键、触发器和复制的限制)
不推荐:认为备份工具自动支持新的 MySQL 主版本
PostgreSQL 高可用
按恢复目标分级:单机加异地备份 → 主备人工切换 → Patroni 自动切换 → Kubernetes 上用 CloudNativePG
不推荐:小规模单机系统直接上三节点自动切换集群;只为跑数据库而引入 Kubernetes
对象存储
单机或附件少时用本地文件系统加元数据表;多机时用 SeaweedFS · SeaweedFS 为 Apache-2.0;Garage 为 AGPLv3;MinIO 为 AGPLv3。分发和对外服务的义务口径见 27
备选:Garage(轻量、多地点自托管;先做 S3 功能 PoC,注意 AGPLv3);Ceph RGW(已有 Ceph 运维团队)
不推荐:MinIO 社区版(新项目);自己写 S3 兼容服务
向量存储
pgvector · pgvector 为 PostgreSQL License
备选:Qdrant(压测后 pgvector 达不到要求,需要独立资源和带过滤的近似检索优化);Milvus(规模很大且团队能运维分布式系统)
不推荐:没有瓶颈就先上独立向量集群
  • PostgreSQL 迁移静态检查:能发现未用 CONCURRENTLY 建索引、未用 NOT VALID 加约束等风险写法;通过检查不等于没有锁风险,大表变更仍要人审(DB-016)。迁移执行工具按语言选,见 10–15。
  • PostgreSQL 备份与时间点恢复:pgBackRest 的仓库曾于 2026-04 被维护者归档,2026-05 在多家公司赞助下恢复维护,2026-10 仍有正式发布;赞助模式可能再变,选用时关注其官网公告。

常见错误

错误做法:「PostgreSQL 的 DDL 是事务性的」所以随时可以执行
应该:设 lock_timeout,大表按扩展-收缩分步做,并估算锁和重写
错误做法:上线时依赖 ORM 自动同步表结构
应该:版本化迁移加人工审查加目标库演练
错误做法:给每个迁移写 down 脚本就当可以回滚
应该:向前修复为主,不可逆迁移标明并确认备份可用
错误做法:在事务里调用大模型或第三方接口
应该:事务里只写 outbox 或任务记录,提交后再调用
错误做法:用 SKIP LOCKED 做普通查询来「避免等锁」
应该:只在任务领取上用;普通查询接受等待或调整锁粒度
错误做法:任务领取只设超时不带 fencing token
应该:每次领取递增 token,写入时校验
错误做法:有主从复制就不做备份
应该:基础备份加 WAL 归档,异地保存,定期恢复演练
错误做法:备份任务显示成功就当能恢复
应该:定期在隔离环境里真的恢复一次并记录结果
错误做法:「兼容 PostgreSQL」的库直接用 PostgreSQL 的 SQL
应该:在目标库上跑方言测试
错误做法:先上专用向量集群以防将来要迁移
应该:先用 pgvector,按压测结果决定

待确认

下面的内容还没核实或待补调研,不要当成规则执行。

  • PgBouncer、Patroni、Qdrant、Milvus、SeaweedFS、Garage、gh-ost、Percona XtraBackup 的许可证未从官方仓库 LICENSE 逐一核实(报告给出 PgBouncer 为 ISC、Patroni 为 MIT、Qdrant 与 Milvus 为 Apache-2.0、SeaweedFS 为 Apache-2.0、Garage 为 AGPLv3、gh-ost 为 MIT)
  • pgBackRest 2026 年归档又恢复维护的经过只有二手报道(官网未见公告),赞助模式的长期稳定性待观察
  • 报告称某个 PostgreSQL 小版本发布说明要求重做全量备份、重建部分索引,未核实原文
  • MySQL 的新版本命名体系(LTS 与 Innovation 发布线)及 Percona XtraBackup 对新 MySQL 主版本的支持情况未核实
  • CloudNativePG 内置备份改走 Barman Cloud 插件的现状未核实
  • 达梦、金仓当前稳定版本与兼容范围未核实
  • 待补调研:现成的 PostgreSQL 队列库(与 18 共同确认归属和默认推荐)
  • 待补调研:恢复演练频率的业界依据(目前只是参考值),以及备份编排工具(与 26 衔接)
  • 待补调研:对象存储上传的病毒扫描工具选型(与 25 衔接)
  • 待补调研:MySQL 侧的隔离级别默认值(可重复读)对 DB-021、DB-022 的影响及间隙锁行为

来源

  1. S01 PostgreSQL: Versioning Policy · PostgreSQL Global Development Group · 官方文档 · 核实于 2026-11-14
  2. S02 PostgreSQL: Transaction Isolation · PostgreSQL Global Development Group · 官方文档 · 核实于 2026-11-14
  3. S03 PostgreSQL: SELECT(The Locking Clause) · PostgreSQL Global Development Group · 官方文档 · 核实于 2026-11-14
  4. S04 pgvector · pgvector · 官方文档 · 核实于 2026-11-14
  5. S05 pgBackRest · pgBackRest · 官方文档 · 核实于 2026-11-14
  6. S06 WAL-G · WAL-G · 官方文档 · 核实于 2026-11-14
  7. S07 Barman · EnterpriseDB · 官方文档 · 核实于 2026-11-14
  8. S08 MinIO(GitHub 仓库) · MinIO · 官方文档 · 核实于 2026-11-14
  9. S09 Squawk · Squawk · 官方文档 · 核实于 2026-11-14
  10. S10 PostgreSQL: Row Security Policies · PostgreSQL Global Development Group · 官方文档 · 核实于 2026-11-13
  11. S11 PostgreSQL: ALTER TABLE · PostgreSQL Global Development Group · 官方文档 · 核实于 2026-11-13
  12. S12 PostgreSQL: CREATE INDEX · PostgreSQL Global Development Group · 官方文档 · 核实于 2026-11-13
  13. S13 PostgreSQL: Explicit Locking · PostgreSQL Global Development Group · 官方文档 · 核实于 2026-11-13
  14. S14 PostgreSQL: Continuous Archiving and Point-in-Time Recovery · PostgreSQL Global Development Group · 官方文档 · 核实于 2026-11-13
  15. S15 PostgreSQL: Table Partitioning · PostgreSQL Global Development Group · 官方文档 · 核实于 2026-11-13
  16. S16 PostgreSQL: UUID Functions · PostgreSQL Global Development Group · 官方文档 · 核实于 2026-11-13
  17. S17 RFC 9562: Universally Unique IDentifiers (UUIDs) · IETF · 标准 · 2024-05 · 核实于 2026-11-14
  18. S18 PostgreSQL: Numeric Types · PostgreSQL Global Development Group · 官方文档 · 核实于 2026-11-14
  19. S19 PostgreSQL: Error Codes · PostgreSQL Global Development Group · 官方文档 · 核实于 2026-11-14
  20. S20 How to do distributed locking · Martin Kleppmann · 社区 · 2016-02-08 · 核实于 2026-11-14
  21. S21 ParallelChange · martinfowler.com · 社区 · 2014-05-13 · 核实于 2026-11-14
  22. S22 Pattern: Transactional outbox · microservices.io · 社区 · 核实于 2026-11-14
  23. S23 PgBouncer FAQ · PgBouncer · 官方文档 · 核实于 2026-11-13
  24. S24 MySQL: InnoDB Online DDL Operations · Oracle · 官方文档 · 核实于 2026-11-13
  25. S25 MySQL: Statements That Cause an Implicit Commit · Oracle · 官方文档 · 核实于 2026-11-14
  26. S26 PostgreSQL: Date/Time Types · PostgreSQL Global Development Group · 官方文档 · 核实于 2026-11-13
  27. S27 Qdrant · Qdrant · 官方文档 · 核实于 2026-11-13
  28. S28 Milvus · Milvus · 官方文档 · 核实于 2026-11-13
  29. S29 SeaweedFS · SeaweedFS · 官方文档 · 核实于 2026-11-13
  30. S30 Garage · Deuxfleurs · 官方文档 · 核实于 2026-11-13
  31. S31 Patroni · Patroni · 官方文档 · 核实于 2026-11-13
  32. S32 CloudNativePG: Barman Cloud Plugin · CloudNativePG · 官方文档 · 核实于 2026-11-13
  33. S33 Amazon S3: Sharing objects with presigned URLs · AWS · 官方文档 · 核实于 2026-11-13
  34. S34 gh-ost · GitHub · 官方文档 · 核实于 2026-11-13
  35. S35 TiDB: MySQL Compatibility · PingCAP · 官方文档 · 核实于 2026-11-13