首页/性能与质量

用数据说话,也把测量条件写清楚

本页所有数字都带测量条件;不明确说条件的性能页,本身就不专业。

本页导航: 测量条件 · 一小时持续压测 · 分操作延迟 · 大报价 · 文档生成 · 全量关键词搜索 · 资源与稳定性 · 其他可用性证据 · 优化过程 · 测试与质量 · 诚实声明

Conditions

先看条件

脱离测量条件的延迟数字没有意义。以下是一次完整压测的全部前置条件;后面每一张表都建立在这些条件之上。 性能实测时间为 2026-09-25。

测量口径

浏览器之外的真实 HTTPS API 请求,经 Nginx 网关并验证测试 CA;计时含 HTTPS + 读取 JSON + 语义校验。 也就是说,这里的每一个毫秒都包含 TLS、网关转发与响应体解析,不是只统计服务端函数耗时。

项值
数据规模 合成租户 perf-capacity-v2:50,000 产品 / 50,000 报价 / 1,005,950 明细行; 另加项目现有 CMM Excel 数据与规则、20 个合成产品方案、50,000 条区域价目
行规模分布 20 行占 49,985 份;50 / 200 / 1,000 行各 5 份
宿主机 MacBookPro16,2,Intel x86_64,8 逻辑 CPU,16 GiB,macOS 14.8.3
运行时 Docker Linux,Linux 6.12.5-linuxkit,Python 3.12.14、PostgreSQL 17.11、Nginx 1.30.5;Docker 分配约 8 GiB
进程配置 2 个 API 容器 × 2 Uvicorn 进程;业务池 8 / 身份池 4;单进程请求并发上限 32;1 Worker + 1 Scheduler(4/2 池)
用户 300 个独立账号(1 管理员 + 299 销售),独立连接池,已登录会话
负载曲线 前 1,800 秒平均思考 10 秒,后 1,800 秒 5 秒
测量口径 浏览器之外的真实 HTTPS API 请求,经 Nginx 网关并验证测试 CA;计时含 HTTPS + 读取 JSON + 语义校验
以上条件与后续所有结论必须同时阅读;缺一项,数字就不可比。
60-minute run

一小时持续压测

3,600.24 秒连续运行,300 个活跃用户执行列表、详情、初始化、首页统计、产品分页、选配、保存与重算, 全程共发出 170,327 次真实 HTTPS 请求。

50,000
产品 / 报价
1,005,950
报价明细行
170,327
总请求数(平均 47.31 req/s)
40.59 ms
P50
231.84 ms
P95
1,191.62 ms
P99
0.00235%
错误率(4 次 503)
阶段请求数错误P50P95P99
前 30 分钟(思考 10 s) 57,594036.12 ms166.82 ms505.49 ms
后 30 分钟(思考 5 s) 112,733443.71 ms274.23 ms1,540.02 ms
后半程思考时间缩短一半,请求量接近翻倍,尾部延迟随之上升——这是负载变化的结果,不是系统退化。
两个必须说清的口径

300 是带思考时间的活跃用户数,不等于每秒 300 请求或 300 并发。 实际稳态吞吐是平均 47.31 req/s。把活跃用户数当作并发数读取,会高估本系统近一个数量级。

4 次 503 集中在第 37 分钟(初始化 1、详情 2、列表 1),均返回明确"服务繁忙" + Retry-After; 之后 23 分钟无新增错误。过载保护按预期动作并给出可重试信号,而不是让请求挂死或静默失败。

Per-operation

分操作延迟

同一小时内的请求按操作拆分。"服务端 P95"只计应用内部耗时,与端到端 P95 的差值即为网关、TLS 与网络序列化开销。

操作请求数P50P95P99服务端 P95
报价列表55,63736.17 ms189.54 ms1,138.87 ms159.98 ms
报价详情43,16631.24 ms176.85 ms892.87 ms142.50 ms
初始化 bootstrap15,87269.36 ms335.78 ms2,125.88 ms300.84 ms
首页统计15,78138.24 ms199.70 ms1,138.75 ms169.86 ms
产品分页16,01031.06 ms166.96 ms938.51 ms133.90 ms
选配联动 最重的操作 12,61996.57 ms388.01 ms1,629.78 ms355.27 ms
保存报价8,04072.53 ms443.52 ms1,706.18 ms396.15 ms
报价重算3,20228.27 ms155.30 ms713.85 ms115.07 ms
选配联动是本轮实测中最重的操作:P95 388.01 ms、P99 1,629.78 ms。它要在一次请求内完成规则求值、兼容校验、 价格解析与 BOM 展开,因此其延迟高于普通读写。若你的现场存在更复杂的规则集或更长的价目链,应以此为基线另行复测。
Large quotes

大报价:按明细行拆分

真正拉开差距的不是报价"张数",而是单张报价的明细行数。下表把保存与重算按 20 / 50 / 200 / 1,000 行分别统计。

明细行保存 P95 / P99重算 P95 / P99
20325.76 / 1,415.04 ms130.85 / 713.85 ms
50354.69 / 1,187.00 ms70.06 / 153.98 ms
200799.09 / 3,232.91 ms147.10 / 306.39 ms
1,0001,758.08 / 6,975.73 ms306.76 / 391.17 ms
保存的 P95 随明细行数上升;重算 P95 未随行数单调递增,且各规模样本量较小,请以阈值判定为准,不做趋势外推。

22 / 22 passed 验收阈值

docs/performance/acceptance-summary.json 中 22 项阈值全部 passed。 阈值是事前设定的判定线,不是事后挑出来的好看数字。

阈值口径

  • 选配:P95 ≤1,000 ms、P99 ≤3,000 ms
  • 各规模保存:P95 ≤2,000 ms
  • 重算:20 / 50 / 200 行 P95 ≤3,000 ms,1,000 行 P95 ≤10,000 ms
  • PDF:20 / 50 / 200 行 P95 ≤10 s,1,000 行 P95 ≤60 s
  • 四类搜索:P95 ≤1,000 ms
Documents

文档生成

以下 PDF 生成与 300 用户在线负载同时进行,计时含入队 → 排队 → 轮询 → 下载的完整端到端耗时, 而不是只统计渲染函数本身。

报价行PDF 页数样本 P50P95最大端到端
202203.133 s5.899 s6.840 s
503103.796 s4.320 s4.954 s
20010104.034 s8.923 s10.817 s
1,0004455.910 s11.668 s13.009 s
端到端口径含入队、排队、轮询与下载四个环节,因此不等于单份文档的渲染耗时。

超大 XLSX

50,000 行 XLSX(含入队 / 等待 / 下载)共 27.191 秒,909,204 字节。

高峰段追加样本

高峰段追加 80 次 20 行 PDF:P50 3.502 s / P95 4.251 s / 最大 4.482 s。

样本处理

两段合计 125 次 PDF,所有样本保留,不挑选较快样本,也不剔除离群值。

未覆盖的场景

本轮为单个 Worker 顺序生成,未测 300 人同时批量生成 PDF。多人同时触发大批量文档时, 排队时间会随队列长度线性累加,不能用上表的端到端数字外推。

Resources

资源与稳定性

基于 159 个有效样本的宿主机与容器资源采样,以及压测全程的数据库连接与事务观测。

宿主 CPU

平均 50.42%,最高采样 86.3%。宿主为 8 逻辑 CPU,全程未出现持续打满或请求堆积。

宿主内存

最低可用内存 5,525 MiB。宿主总内存 16 GiB,其中 Docker 分配约 8 GiB。

数据库侧

应用连接最多观测 52 个;最老事务 0.876 秒;死锁 0;无容器重启或 OOM。

容器内存

容器最高采样内存最后采样内存
API 1(两进程)674.10 MiB658.00 MiB
API 2(两进程)669.20 MiB644.70 MiB
Worker118.80 MiB30.19 MiB
调度器39.16 MiB36.43 MiB
网关15.83 MiB14.33 MiB
PostgreSQL621.40 MiB603.40 MiB
每个 API 容器含 2 个 Uvicorn 进程,内存为容器内合计值。
诚实边界:内存增长

API 内存从初期 ~300 MiB 增至末期 ~650 MiB, 一小时结果不能证明长期无内存增长。若要支撑长时间连续运行,需要做更长时间的驻留观测与泄漏排查, 本轮数据不足以支撑"长期稳定"的结论。

Also measured

其他可用性证据

除了延迟与错误率,我们还观测了报表、冷启动、容器替换、数据一致性与大目录打开这几类常见"上线后才暴露"的问题。

RPT

报表

50,000 行 CSV 入队后 6.658 秒完成;汇总 10.310 秒,金额与数据库一致。

COLD

冷启动

在线负载期间新建双进程 API 容器,首个进程就绪 6.871 s,双进程全就绪 7.039 s。

SWAP

容器替换

API 1 移除重建、IP 变化后,网关容器不变、登录会话持续有效,新实例 0.876 秒后被观测到已处理请求。

DATA

数据一致性

压测后核对 50,000 报价 + 50,000 摘要索引 + 1,005,950 明细;300 份活跃报价版本 / 金额及 10,760 条明细索引与快照一致。

BIG

大目录专项

2026-09-27,520 产品 / 501 行报价用例:目录发布 168 ms、报价创建 286 ms、页面打开 523 ms、CSV 导出 79 ms; 金额未税 ¥50,100 / 税额 ¥6,513 / 含税 ¥56,613。

隔离环境单次观测 不能据此推断生产 P95 或并发上限。

How we got here

我们做了哪些优化

下面的每一条都有前后对照,但我们不作倍数承诺:这些是在特定数据量、特定硬件与特定规则集下消除的瓶颈, 换一个现场需要重新测量,而不是直接套用。

权限查询

最初的权限查询导致约 12.7% 请求失败、P95 约 12.4 秒。改为索引查询 + 只读价格缓存后消除主要开销。

SQL 与事务次数

六个代表性列表 / 首页 / 详情操作的仓储 SQL 52 → 36 次、事务 26 → 16 次。

连接池与过载保护

原 4/2 池 + 16 处理名额时 3.22% 请求被过载保护拒绝;调到 8/4 与 32 后,三分钟高峰复测 11,387 次请求零错误。

PDF 子进程

20 行 PDF 曾 P95 10.301 秒(超标)。定位为任务子进程重复编译 Python 源码,改为镜像构建期 compileall 后达标。

Quality

测试与质量保障

2026-09-27 全量回归结果。测试在系统临时目录建库,读取 data/source/ 原始客户样本,不改写用户运行数据。

范围结果耗时
后端业务与接口895 / 895 通过271.499 s
前端逻辑180 / 180 通过1,649.9 ms
浏览器回归(真实接口,非 mock)35 / 35 通过2.8 分钟
构建与格式Prettier + Vite 构建通过;8765 / 8766 只读健康检查 HTTP 200—
浏览器回归使用真实接口,不打桩、不 mock 后端响应。

后端行覆盖率 94%

docs/full-coverage.txt:2,203 stmts / 130 miss。 app.py 90%、catalog.py 96%、engine.py 98%、 documents.py 99%、config_tables.py 100%、security.py 93%。

SaaS 新增 18 模块

行覆盖 89%,分支覆盖 83%(1,906 stmts / 210 miss)。

CLI / MCP 新增包

覆盖率 90%。

测试资产

tests/*.py 109 个 + tests/*.test.js 45 个 + Playwright spec 19 个。

覆盖的 correctness 场景

288 个组合逐项匹配、1/2/4 倍 BOM、Decimal 尾差分摊、不可折扣项、导入原子性。

覆盖的流程与输出场景

审批、版本冲突、订单锁定、文档格式。

Boundaries

诚实声明

性能页最容易越界的地方,是把一次本机测量说成一份对外承诺。以下是本页数字不能被解读成的东西。

本次不将本机测试转换为生产 SLA

上述全部结果来自本机 Docker 单机上的合成负载实测,用于说明系统在给定条件下的行为与容量量级, 不是生产服务等级承诺。P95 231.84 ms 是本次测量条件下的观测值,不能作为对外 SLA 引用。

未测:中国办公网络、同时登录突发、SSO、跨地域数据库、真实外围系统耗时、300 人同时批量生成 PDF。

部署形态 单主机参考部署

  • Compose 是单主机参考部署,不宣称主机级高可用
  • 租户隔离由应用层实施,未使用数据库 RLS
  • 未实现自助开通与 SaaS 计费
  • RPO 24 小时 / RTO 4 小时仍是部署演练目标,不是已达成的 SLA

容量边界 报告导出

  • 报告导出的容量边界:500,000 / 200,000 / 5,000 行
  • 该边界为既定容量口径,本轮实测未覆盖该量级(本轮最大实测为 50,000 行 XLSX)
  • 超出边界的导出需按实际数据量与并发数另行验证,不能由本页数字外推

把测量条件一起带走

如果你要复现这些数字,或者想用自己最复杂的那几份报价跑一遍,我们可以提供完整的压测脚本与环境清单。 评估性能最有说服力的方式,从来不是接受别人给的数字,而是自己测一次。