自动审计的 PostgreSQL 性能记录
2026-10-15,在指定 PostgreSQL 测试环境比较普通上下文、审计关闭和审计开启。功能与事务验证通过,性能目标未通过。 当前数据不能支持“开启审计后普通 CRUD 吞吐/P95 损失不超过 10%”或“无审计变化时不超过 5%”的结论。
测量客户端为 Windows 10.0.19045、.NET 10.0.13、6 个逻辑处理器;服务器 PostgreSQL 16.15,通过局域网连接。服务器 CPU、磁盘和同时运行的其他业务负载未测定。指定测试库最初不存在,因此显式初始化了该专用库;测试结束只删除每轮创建的临时 schema,数据库保留。连接串与凭据没有进入报告。
最终版本的定向比较
最终版本默认生成 UUID v7 操作/明细 ID。下面每种模式均为并发 4、每线程预热 24 次,测量 3 轮,每轮每线程 96 次,共 1,152 次。三种模式轮换执行顺序,业务查询与显式事务相同。
| 场景/模式 | ops/s | P50 ms | P95 ms | P99 ms | 近似分配 bytes/op |
|---|---|---|---|---|---|
| 单实体/普通上下文 | 480.7 | 3.41 | 34.55 | 77.14 | 65,862 |
| 单实体/审计关闭 | 485.1 | 3.50 | 25.41 | 74.52 | 73,884 |
| 单实体/审计开启 | 190.9 | 16.95 | 72.11 | 101.48 | 94,107 |
| 三表四实体/普通上下文 | 199.5 | 5.31 | 79.74 | 146.29 | 99,380 |
| 三表四实体/审计关闭 | 261.6 | 4.66 | 42.13 | 192.97 | 107,502 |
| 三表四实体/审计开启 | 88.6 | 33.45 | 115.77 | 229.28 | 176,860 |
以同一审计基类的开关比较,单实体开启后吞吐下降约 60.7%,P95 增加约 183.8%;三表四实体吞吐下降约 66.1%,P95 增加约 174.8%。普通上下文是补充基线,用来观察接入基类本身的成本;它与关闭模式的尾延迟也有明显差异,说明环境存在波动,不宜给这些百分比作精确的长期承诺。原始汇总见 UUID v7 定向结果。
耗时在哪里
| 场景/模式 | 到 SavingChanges 回调平均 ms | SaveChanges 平均 ms | Commit 平均 ms |
|---|---|---|---|
| 单实体/审计关闭 | <0.01 | 1.89 | 4.53 |
| 单实体/审计开启 | 0.07 | 2.31 | 16.83 |
| 三表四实体/审计关闭 | <0.01 | 1.85 | 11.43 |
| 三表四实体/审计开启 | 0.07 | 2.54 | 40.02 |
开启模式在 SavingChanges 回调前完成变更检测、差异序列化和审计实体附加,因此第一列可以观察这部分采集成本。普通 EF 的检测发生在该回调之后,不能直接把第一列相减作为两种实现的纯 CPU 差异。这里的平均数也不代表采集阶段的 P95。
单实体每操作 2 次 EF 命令执行,三表场景 3 次,三种模式相同。这不意味着写入量相同:Npgsql 在一个保存批次中执行业务及审计语句。单实体多一个审计 INSERT;三表场景多四个,且每条明细有主键和两个查询索引。每个测量块均校验实际新增审计条数,不能通过漏写来降低成本。
服务器使用 fsync=on、full_page_writes=on、synchronous_commit=on、wal_sync_method=fdatasync,没有配置同步 standby 名称。测试未修改这些设置。观测到的主要增量集中在提交阶段,符合增加持久写入后的成本特征;但 track_wal_io_timing=off,也没有服务器磁盘和复制过程的观测,尚不能把具体根因归结为某块磁盘或某个 PostgreSQL 参数。
UUID v7 改善键的时间局部性,但这轮结果仍未达到目标。两轮之间存在环境变化,不能仅凭单实体吞吐上升就断言收益完全来自 ID 格式。仍保留同事务、完整选定字段和持久化语义,没有使用异步审计、无日志表或关闭同步提交来换取通过。
完整场景的诊断轮
下面是 UUID v4 版本的耗时分解轮,覆盖无变化、未选字段、单实体、多表和跟踪 1,000 条实体。它用于诊断,不代表最终 UUID v7 版本已对全部场景重新验收。每项每模式样本数:并发 1 为 288,并发 4 为 1,152。
| 场景 | 并发 | 普通 ops/s / P95 ms | 关闭 ops/s / P95 ms | 开启 ops/s / P95 ms |
|---|---|---|---|---|
| 无变化 | 1 | 316.7 / 12.06 | 430.1 / 6.72 | 375.3 / 12.17 |
| 无变化 | 4 | 1753.2 / 4.81 | 1161.1 / 8.26 | 1306.2 / 6.15 |
| 未选字段 | 1 | 229.9 / 11.32 | 202.4 / 13.74 | 185.8 / 17.46 |
| 未选字段 | 4 | 671.2 / 24.18 | 621.9 / 24.62 | 484.3 / 33.26 |
| 单实体 | 1 | 265.0 / 8.21 | 217.9 / 16.22 | 142.0 / 26.22 |
| 单实体 | 4 | 460.1 / 47.60 | 473.6 / 27.43 | 142.5 / 99.38 |
| 三表四实体 | 1 | 128.2 / 24.44 | 91.7 / 36.72 | 33.8 / 105.53 |
| 三表四实体 | 4 | 257.4 / 71.95 | 236.3 / 65.10 | 69.7 / 184.48 |
| 跟踪 1,000 条改一条 | 1 | 145.5 / 16.18 | 114.4 / 20.33 | 66.7 / 55.80 |
| 跟踪 1,000 条改一条 | 4 | 180.1 / 79.04 | 271.3 / 31.33 | 132.8 / 70.30 |
跟踪 1,000 条时,开启模式在保存回调前平均约 0.82ms(并发 1)/1.99ms(并发 4),大部分实体未发生变化也有检查成本。业务只需要修改一条时,应避免为了本次操作跟踪无关的大量实体;这项场景仍保留,用来暴露接入成本。原始汇总见 完整诊断结果。
证据范围与后续验收
PostgreSQL 上验证了三表四实体一起提交,以及外层回滚同时撤销业务和审计;SQLite 回归还覆盖缺失身份、超限、取消、保存回调失败、约束失败重试、附加实体原值未知和同步增删改。Lab API 验证覆盖多表效果、同操作归组及用户隔离。事务正确性不等于性能达标。
下一次性能验收应先同时记录服务器 WAL 同步/磁盘延迟和其他负载,确定提交成本来源,再决定索引、批量事务大小或服务器配置的取舍。改变事务批量大小必须同时检查业务延迟与锁持有时间。当前实现适合继续功能 review;高 TPS 场景的性能验收仍未完成,不能按已达标交付生产容量承诺。复现参数和测量边界见 基准程序 README。