Commit c51d254fdc9e738d3cef93c1d0755c929a1a63c3
1 parent
d71e20f0
性能测试
Showing
7 changed files
with
98 additions
and
9 deletions
Show diff stats
config/config.yaml
| @@ -114,7 +114,7 @@ function_score: | @@ -114,7 +114,7 @@ function_score: | ||
| 114 | # 重排配置(provider/URL 在 services.rerank) | 114 | # 重排配置(provider/URL 在 services.rerank) |
| 115 | rerank: | 115 | rerank: |
| 116 | enabled: true | 116 | enabled: true |
| 117 | - rerank_window: 1000 | 117 | + rerank_window: 384 |
| 118 | timeout_sec: 15.0 | 118 | timeout_sec: 15.0 |
| 119 | weight_es: 0.4 | 119 | weight_es: 0.4 |
| 120 | weight_ai: 0.6 | 120 | weight_ai: 0.6 |
config/config_loader.py
| @@ -107,7 +107,7 @@ class RankingConfig: | @@ -107,7 +107,7 @@ class RankingConfig: | ||
| 107 | class RerankConfig: | 107 | class RerankConfig: |
| 108 | """重排配置(provider/URL 在 services.rerank)""" | 108 | """重排配置(provider/URL 在 services.rerank)""" |
| 109 | enabled: bool = True | 109 | enabled: bool = True |
| 110 | - rerank_window: int = 1000 | 110 | + rerank_window: int = 384 |
| 111 | timeout_sec: float = 15.0 | 111 | timeout_sec: float = 15.0 |
| 112 | weight_es: float = 0.4 | 112 | weight_es: float = 0.4 |
| 113 | weight_ai: float = 0.6 | 113 | weight_ai: float = 0.6 |
| @@ -312,7 +312,7 @@ class ConfigLoader: | @@ -312,7 +312,7 @@ class ConfigLoader: | ||
| 312 | rerank_data = config_data.get("rerank", {}) | 312 | rerank_data = config_data.get("rerank", {}) |
| 313 | rerank = RerankConfig( | 313 | rerank = RerankConfig( |
| 314 | enabled=bool(rerank_data.get("enabled", True)), | 314 | enabled=bool(rerank_data.get("enabled", True)), |
| 315 | - rerank_window=int(rerank_data.get("rerank_window", 1000)), | 315 | + rerank_window=int(rerank_data.get("rerank_window", 384)), |
| 316 | timeout_sec=float(rerank_data.get("timeout_sec", 15.0)), | 316 | timeout_sec=float(rerank_data.get("timeout_sec", 15.0)), |
| 317 | weight_es=float(rerank_data.get("weight_es", 0.4)), | 317 | weight_es=float(rerank_data.get("weight_es", 0.4)), |
| 318 | weight_ai=float(rerank_data.get("weight_ai", 0.6)), | 318 | weight_ai=float(rerank_data.get("weight_ai", 0.6)), |
docs/性能测试报告.md
| @@ -72,6 +72,9 @@ curl -u 'saas:4hOaLaf41y2VuI8y' -X GET 'http://localhost:9200/search_products_te | @@ -72,6 +72,9 @@ curl -u 'saas:4hOaLaf41y2VuI8y' -X GET 'http://localhost:9200/search_products_te | ||
| 72 | }' | 72 | }' |
| 73 | ``` | 73 | ``` |
| 74 | 74 | ||
| 75 | +主要配置: | ||
| 76 | +rerank_window=384 | ||
| 77 | + | ||
| 75 | ## 5. 执行前准备(可复现步骤) | 78 | ## 5. 执行前准备(可复现步骤) |
| 76 | 79 | ||
| 77 | ### 5.1 环境与依赖 | 80 | ### 5.1 环境与依赖 |
| @@ -228,3 +231,89 @@ cd /data/saas-search | @@ -228,3 +231,89 @@ cd /data/saas-search | ||
| 228 | 231 | ||
| 229 | - 压测脚本:`scripts/perf_api_benchmark.py` | 232 | - 压测脚本:`scripts/perf_api_benchmark.py` |
| 230 | - 本次结果:`perf_reports/2026-03-12/perf_matrix_report.json` | 233 | - 本次结果:`perf_reports/2026-03-12/perf_matrix_report.json` |
| 234 | +- Search 多租户补测:`perf_reports/2026-03-12/search_tenant_matrix/` | ||
| 235 | + | ||
| 236 | +## 12. Search 多租户补测(2026-03-12) | ||
| 237 | + | ||
| 238 | +补测背景: | ||
| 239 | +- 原报告中的 search 数据来自 `tenant_id=162` | ||
| 240 | +- 该租户文档量偏小,无法覆盖不同数据规模下的性能趋势 | ||
| 241 | +- 本节补充 `backend_search` 在不同租户文档规模上的对比结果 | ||
| 242 | + | ||
| 243 | +租户与文档数(按需求方提供): | ||
| 244 | +- `tenant 0`: `10000` | ||
| 245 | +- `tenant 1`: `500` | ||
| 246 | +- `tenant 2`: `1000` | ||
| 247 | +- `tenant 3`: `2000` | ||
| 248 | +- `tenant 4`: `5000` | ||
| 249 | + | ||
| 250 | +测试口径: | ||
| 251 | +- 场景:`backend_search`(`POST /search/`) | ||
| 252 | +- 并发:`1 / 5 / 10 / 20` | ||
| 253 | +- 每档时长:`20s` | ||
| 254 | +- 执行时间:`2026-03-12 10:15:01` 到 `2026-03-12 10:22:26`(CST) | ||
| 255 | + | ||
| 256 | +执行命令(逐租户复现): | ||
| 257 | + | ||
| 258 | +```bash | ||
| 259 | +cd /data/saas-search | ||
| 260 | +mkdir -p perf_reports/2026-03-12/search_tenant_matrix | ||
| 261 | +for t in 0 1 2 3 4; do | ||
| 262 | + .venv/bin/python scripts/perf_api_benchmark.py \ | ||
| 263 | + --scenario backend_search \ | ||
| 264 | + --concurrency-list 1,5,10,20 \ | ||
| 265 | + --duration 20 \ | ||
| 266 | + --tenant-id ${t} \ | ||
| 267 | + --backend-base http://127.0.0.1:6002 \ | ||
| 268 | + --embedding-base http://127.0.0.1:6005 \ | ||
| 269 | + --translator-base http://127.0.0.1:6006 \ | ||
| 270 | + --reranker-base http://127.0.0.1:6007 \ | ||
| 271 | + --output perf_reports/2026-03-12/search_tenant_matrix/tenant_${t}.json | ||
| 272 | +done | ||
| 273 | +``` | ||
| 274 | + | ||
| 275 | +结果文件: | ||
| 276 | +- `perf_reports/2026-03-12/search_tenant_matrix/tenant_0.json` | ||
| 277 | +- `perf_reports/2026-03-12/search_tenant_matrix/tenant_1.json` | ||
| 278 | +- `perf_reports/2026-03-12/search_tenant_matrix/tenant_2.json` | ||
| 279 | +- `perf_reports/2026-03-12/search_tenant_matrix/tenant_3.json` | ||
| 280 | +- `perf_reports/2026-03-12/search_tenant_matrix/tenant_4.json` | ||
| 281 | + | ||
| 282 | +### 12.1 按租户汇总(overall) | ||
| 283 | + | ||
| 284 | +| Tenant | 文档数 | 总请求 | 总成功率 | 汇总RPS | 加权平均延迟(ms) | | ||
| 285 | +|---:|---:|---:|---:|---:|---:| | ||
| 286 | +| 0 | 10000 | 195 | 87.18% | 2.00 | 4644.66 | | ||
| 287 | +| 1 | 500 | 548 | 100.00% | 6.73 | 1346.28 | | ||
| 288 | +| 2 | 1000 | 449 | 100.00% | 5.32 | 1731.96 | | ||
| 289 | +| 3 | 2000 | 314 | 100.00% | 3.58 | 2605.60 | | ||
| 290 | +| 4 | 5000 | 256 | 100.00% | 2.76 | 3491.67 | | ||
| 291 | + | ||
| 292 | +### 12.2 按并发明细(backend_search) | ||
| 293 | + | ||
| 294 | +| Tenant | 文档数 | 并发 | 请求数 | 成功率 | 吞吐(RPS) | Avg(ms) | P95(ms) | Max(ms) | | ||
| 295 | +|---:|---:|---:|---:|---:|---:|---:|---:|---:| | ||
| 296 | +| 0 | 10000 | 1 | 42 | 100.00% | 2.09 | 475.85 | 893.82 | 1034.47 | | ||
| 297 | +| 0 | 10000 | 5 | 41 | 100.00% | 1.91 | 2558.69 | 4109.76 | 4532.22 | | ||
| 298 | +| 0 | 10000 | 10 | 51 | 100.00% | 1.94 | 4816.46 | 5250.89 | 6729.64 | | ||
| 299 | +| 0 | 10000 | 20 | 61 | 59.02% | 2.05 | 8773.39 | 10037.65 | 10156.94 | | ||
| 300 | +| 1 | 500 | 1 | 120 | 100.00% | 5.99 | 166.04 | 312.03 | 1064.41 | | ||
| 301 | +| 1 | 500 | 5 | 136 | 100.00% | 6.76 | 735.79 | 1291.69 | 1543.71 | | ||
| 302 | +| 1 | 500 | 10 | 151 | 100.00% | 7.37 | 1349.03 | 2014.22 | 2020.32 | | ||
| 303 | +| 1 | 500 | 20 | 141 | 100.00% | 6.77 | 2936.65 | 5295.54 | 5313.42 | | ||
| 304 | +| 2 | 1000 | 1 | 110 | 100.00% | 5.47 | 182.13 | 353.14 | 465.31 | | ||
| 305 | +| 2 | 1000 | 5 | 106 | 100.00% | 5.18 | 960.47 | 1333.72 | 2163.98 | | ||
| 306 | +| 2 | 1000 | 10 | 112 | 100.00% | 5.26 | 1868.63 | 2352.51 | 2964.84 | | ||
| 307 | +| 2 | 1000 | 20 | 121 | 100.00% | 5.38 | 3690.26 | 4101.46 | 4112.53 | | ||
| 308 | +| 3 | 2000 | 1 | 68 | 100.00% | 3.39 | 294.03 | 650.96 | 1704.33 | | ||
| 309 | +| 3 | 2000 | 5 | 85 | 100.00% | 3.99 | 1237.05 | 1663.36 | 2144.39 | | ||
| 310 | +| 3 | 2000 | 10 | 80 | 100.00% | 3.56 | 2748.00 | 4102.92 | 4463.57 | | ||
| 311 | +| 3 | 2000 | 20 | 81 | 100.00% | 3.41 | 5841.66 | 8352.47 | 8356.66 | | ||
| 312 | +| 4 | 5000 | 1 | 60 | 100.00% | 2.92 | 341.68 | 650.83 | 838.99 | | ||
| 313 | +| 4 | 5000 | 5 | 56 | 100.00% | 2.70 | 1813.34 | 2339.49 | 3073.57 | | ||
| 314 | +| 4 | 5000 | 10 | 60 | 100.00% | 2.48 | 3900.04 | 5529.86 | 5533.53 | | ||
| 315 | +| 4 | 5000 | 20 | 80 | 100.00% | 2.93 | 6722.73 | 7613.28 | 7620.42 | | ||
| 316 | + | ||
| 317 | +异常说明: | ||
| 318 | +- `tenant 0` 在并发 `20` 出现 `ReadTimeout`(25 次),该档成功率下降到 `59.02%` | ||
| 319 | +- 其他租户在本轮口径下均为 `100%` 成功率 |
docs/搜索API对接指南.md
| @@ -225,7 +225,7 @@ response = requests.post(url, headers=headers, json={"query": "芭比娃娃"}) | @@ -225,7 +225,7 @@ response = requests.post(url, headers=headers, json={"query": "芭比娃娃"}) | ||
| 225 | | `min_score` | float | N | null | 最小相关性分数阈值 | | 225 | | `min_score` | float | N | null | 最小相关性分数阈值 | |
| 226 | | `sku_filter_dimension` | array[string] | N | null | 子SKU筛选维度列表(见[SKU筛选维度](#35-sku筛选维度)) | | 226 | | `sku_filter_dimension` | array[string] | N | null | 子SKU筛选维度列表(见[SKU筛选维度](#35-sku筛选维度)) | |
| 227 | | `debug` | boolean | N | false | 是否返回调试信息 | | 227 | | `debug` | boolean | N | false | 是否返回调试信息 | |
| 228 | -| `enable_rerank` | boolean/null | N | null | 是否开启重排(调用外部重排服务对 ES 结果进行二次排序)。不传/传 null 使用服务端 `rerank.enabled`(默认开启)。开启后会先对 ES Top1000(`rerank_window`)重排,再按分页截取;若 `from+size>1000`,则不重排,直接按分页从 ES 返回 | | 228 | +| `enable_rerank` | boolean/null | N | null | 是否开启重排(调用外部重排服务对 ES 结果进行二次排序)。不传/传 null 使用服务端 `rerank.enabled`(默认开启)。开启后会先对 ES TopN(`rerank_window`)重排,再按分页截取;若 `from+size>1000`,则不重排,直接按分页从 ES 返回 | |
| 229 | | `rerank_query_template` | string | N | null | 重排 query 模板(可选)。支持 `{query}` 占位符;不传则使用服务端配置 | | 229 | | `rerank_query_template` | string | N | null | 重排 query 模板(可选)。支持 `{query}` 占位符;不传则使用服务端配置 | |
| 230 | | `rerank_doc_template` | string | N | null | 重排 doc 模板(可选)。支持 `{title} {brief} {vendor} {description} {category_path}`;不传则使用服务端配置 | | 230 | | `rerank_doc_template` | string | N | null | 重排 doc 模板(可选)。支持 `{title} {brief} {vendor} {description} {category_path}`;不传则使用服务端配置 | |
| 231 | | `user_id` | string | N | null | 用户ID(用于个性化,预留) | | 231 | | `user_id` | string | N | null | 用户ID(用于个性化,预留) | |
search/searcher.py
| @@ -282,7 +282,7 @@ class Searcher: | @@ -282,7 +282,7 @@ class Searcher: | ||
| 282 | # 重排开关优先级:请求参数显式传值 > 服务端配置(默认开启) | 282 | # 重排开关优先级:请求参数显式传值 > 服务端配置(默认开启) |
| 283 | rerank_enabled_by_config = bool(rc.enabled) | 283 | rerank_enabled_by_config = bool(rc.enabled) |
| 284 | do_rerank = rerank_enabled_by_config if enable_rerank is None else bool(enable_rerank) | 284 | do_rerank = rerank_enabled_by_config if enable_rerank is None else bool(enable_rerank) |
| 285 | - rerank_window = rc.rerank_window or 1000 | 285 | + rerank_window = rc.rerank_window |
| 286 | # 若开启重排且请求范围在窗口内:从 ES 取前 rerank_window 条、重排后再按 from/size 分页;否则不重排,按原 from/size 查 ES | 286 | # 若开启重排且请求范围在窗口内:从 ES 取前 rerank_window 条、重排后再按 from/size 分页;否则不重排,按原 from/size 查 ES |
| 287 | in_rerank_window = do_rerank and (from_ + size) <= rerank_window | 287 | in_rerank_window = do_rerank and (from_ + size) <= rerank_window |
| 288 | es_fetch_from = 0 if in_rerank_window else from_ | 288 | es_fetch_from = 0 if in_rerank_window else from_ |
tests/conftest.py
| @@ -191,7 +191,7 @@ def temp_config_file() -> Generator[str, None, None]: | @@ -191,7 +191,7 @@ def temp_config_file() -> Generator[str, None, None]: | ||
| 191 | "functions": [] | 191 | "functions": [] |
| 192 | }, | 192 | }, |
| 193 | "rerank": { | 193 | "rerank": { |
| 194 | - "rerank_window": 1000 | 194 | + "rerank_window": 400 |
| 195 | } | 195 | } |
| 196 | } | 196 | } |
| 197 | 197 |
tests/test_search_rerank_window.py
| @@ -136,7 +136,7 @@ class _FakeESClient: | @@ -136,7 +136,7 @@ class _FakeESClient: | ||
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | 138 | ||
| 139 | -def _build_search_config(*, rerank_enabled: bool = True, rerank_window: int = 1000): | 139 | +def _build_search_config(*, rerank_enabled: bool = True, rerank_window: int = 384): |
| 140 | return SearchConfig( | 140 | return SearchConfig( |
| 141 | field_boosts={"title.en": 3.0}, | 141 | field_boosts={"title.en": 3.0}, |
| 142 | indexes=[IndexConfig(name="default", label="default", fields=["title.en"])], | 142 | indexes=[IndexConfig(name="default", label="default", fields=["title.en"])], |
| @@ -171,7 +171,7 @@ def test_config_loader_rerank_enabled_defaults_true(tmp_path: Path): | @@ -171,7 +171,7 @@ def test_config_loader_rerank_enabled_defaults_true(tmp_path: Path): | ||
| 171 | "spu_config": {"enabled": False}, | 171 | "spu_config": {"enabled": False}, |
| 172 | "ranking": {"expression": "bm25()", "description": "test"}, | 172 | "ranking": {"expression": "bm25()", "description": "test"}, |
| 173 | "function_score": {"score_mode": "sum", "boost_mode": "multiply", "functions": []}, | 173 | "function_score": {"score_mode": "sum", "boost_mode": "multiply", "functions": []}, |
| 174 | - "rerank": {"rerank_window": 1000}, | 174 | + "rerank": {"rerank_window": 384}, |
| 175 | } | 175 | } |
| 176 | config_path = tmp_path / "config.yaml" | 176 | config_path = tmp_path / "config.yaml" |
| 177 | config_path.write_text(yaml.safe_dump(config_data), encoding="utf-8") | 177 | config_path.write_text(yaml.safe_dump(config_data), encoding="utf-8") |
| @@ -283,7 +283,7 @@ def test_searcher_skips_rerank_when_request_explicitly_false(monkeypatch): | @@ -283,7 +283,7 @@ def test_searcher_skips_rerank_when_request_explicitly_false(monkeypatch): | ||
| 283 | 283 | ||
| 284 | def test_searcher_skips_rerank_when_page_exceeds_window(monkeypatch): | 284 | def test_searcher_skips_rerank_when_page_exceeds_window(monkeypatch): |
| 285 | es_client = _FakeESClient() | 285 | es_client = _FakeESClient() |
| 286 | - searcher = _build_searcher(_build_search_config(rerank_enabled=True, rerank_window=1000), es_client) | 286 | + searcher = _build_searcher(_build_search_config(rerank_enabled=True, rerank_window=384), es_client) |
| 287 | context = create_request_context(reqid="t3", uid="u3") | 287 | context = create_request_context(reqid="t3", uid="u3") |
| 288 | 288 | ||
| 289 | monkeypatch.setattr( | 289 | monkeypatch.setattr( |