Commit c51d254fdc9e738d3cef93c1d0755c929a1a63c3

Authored by tangwang
1 parent d71e20f0

性能测试

config/config.yaml
... ... @@ -114,7 +114,7 @@ function_score:
114 114 # 重排配置(provider/URL 在 services.rerank)
115 115 rerank:
116 116 enabled: true
117   - rerank_window: 1000
  117 + rerank_window: 384
118 118 timeout_sec: 15.0
119 119 weight_es: 0.4
120 120 weight_ai: 0.6
... ...
config/config_loader.py
... ... @@ -107,7 +107,7 @@ class RankingConfig:
107 107 class RerankConfig:
108 108 """重排配置(provider/URL 在 services.rerank)"""
109 109 enabled: bool = True
110   - rerank_window: int = 1000
  110 + rerank_window: int = 384
111 111 timeout_sec: float = 15.0
112 112 weight_es: float = 0.4
113 113 weight_ai: float = 0.6
... ... @@ -312,7 +312,7 @@ class ConfigLoader:
312 312 rerank_data = config_data.get("rerank", {})
313 313 rerank = RerankConfig(
314 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 316 timeout_sec=float(rerank_data.get("timeout_sec", 15.0)),
317 317 weight_es=float(rerank_data.get("weight_es", 0.4)),
318 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 72 }'
73 73 ```
74 74  
  75 +主要配置:
  76 +rerank_window=384
  77 +
75 78 ## 5. 执行前准备(可复现步骤)
76 79  
77 80 ### 5.1 环境与依赖
... ... @@ -228,3 +231,89 @@ cd /data/saas-search
228 231  
229 232 - 压测脚本:`scripts/perf_api_benchmark.py`
230 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 225 | `min_score` | float | N | null | 最小相关性分数阈值 |
226 226 | `sku_filter_dimension` | array[string] | N | null | 子SKU筛选维度列表(见[SKU筛选维度](#35-sku筛选维度)) |
227 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 229 | `rerank_query_template` | string | N | null | 重排 query 模板(可选)。支持 `{query}` 占位符;不传则使用服务端配置 |
230 230 | `rerank_doc_template` | string | N | null | 重排 doc 模板(可选)。支持 `{title} {brief} {vendor} {description} {category_path}`;不传则使用服务端配置 |
231 231 | `user_id` | string | N | null | 用户ID(用于个性化,预留) |
... ...
search/searcher.py
... ... @@ -282,7 +282,7 @@ class Searcher:
282 282 # 重排开关优先级:请求参数显式传值 > 服务端配置(默认开启)
283 283 rerank_enabled_by_config = bool(rc.enabled)
284 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 286 # 若开启重排且请求范围在窗口内:从 ES 取前 rerank_window 条、重排后再按 from/size 分页;否则不重排,按原 from/size 查 ES
287 287 in_rerank_window = do_rerank and (from_ + size) <= rerank_window
288 288 es_fetch_from = 0 if in_rerank_window else from_
... ...
tests/conftest.py
... ... @@ -191,7 +191,7 @@ def temp_config_file() -&gt; Generator[str, None, None]:
191 191 "functions": []
192 192 },
193 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 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 140 return SearchConfig(
141 141 field_boosts={"title.en": 3.0},
142 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 171 "spu_config": {"enabled": False},
172 172 "ranking": {"expression": "bm25()", "description": "test"},
173 173 "function_score": {"score_mode": "sum", "boost_mode": "multiply", "functions": []},
174   - "rerank": {"rerank_window": 1000},
  174 + "rerank": {"rerank_window": 384},
175 175 }
176 176 config_path = tmp_path / "config.yaml"
177 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 283  
284 284 def test_searcher_skips_rerank_when_page_exceeds_window(monkeypatch):
285 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 287 context = create_request_context(reqid="t3", uid="u3")
288 288  
289 289 monkeypatch.setattr(
... ...