From 208e079afcaceab40b086e3c1dbb0c3140dd8cc3 Mon Sep 17 00:00:00 2001 From: tangwang Date: Sat, 14 Mar 2026 21:37:51 +0800 Subject: [PATCH] TODO --- docs/TODO.txt | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------ docs/工作总结-微服务性能优化与架构.md | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ docs/性能测试报告.md | 2 +- 3 files changed, 337 insertions(+), 13 deletions(-) create mode 100644 docs/工作总结-微服务性能优化与架构.md diff --git a/docs/TODO.txt b/docs/TODO.txt index 682c2fd..eb9ef40 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -4,6 +4,59 @@ +融合打分: +1. 融合公式 +def fuse_scores(self, context: SearchContext) -> None: + for result in context.results: + # 计算文本相关性分数 + text_score = ( + result.es_base_score * 0.5 + + result.es_phrase_score * 0.3 + + result.es_keywords_score * 0.2 + ) + + # 最终融合分数 + result.fused_score = ( + (result.rerank_text + 0.00001) ** 1.0 * + (result.rerank_boost + 0.1) ** 1.0 * + (result.es_knn_score + 0.6) ** 0.2 * + (text_score + 0.1) ** 0.77 + ) + +2. matched_queries + + # Add debug information if matched_queries is present and from_search_debug is True + if 'matched_queries' in hit: + # Extract individual scores from matched_queries + matched_queries = hit['matched_queries'] + if context.from_search_debug: + result.matched_queries = matched_queries + if isinstance(matched_queries, dict): + result.es_knn_score = matched_queries.get('knn_query', 0.0) + result.es_phrase_score = matched_queries.get('phrase_query', 0.0) + result.es_base_score = matched_queries.get('base_query', 0.0) + result.es_keywords_score = matched_queries.get('keywords_query', 0.0) + result.es_tags_score = matched_queries.get('tags_query', 0.0) + elif isinstance(matched_queries, list): + for query in matched_queries: + if query == 'knn_query': + result.es_knn_score = 1.0 + elif query == 'phrase_query': + result.es_phrase_score = 1.0 + elif query == 'base_query': + result.es_base_score = 1.0 + elif query == 'keywords_query': + result.es_keywords_score = 1.0 + elif query == 'tags_query': + result.es_tags_score = 1.0 + +es_query["track_scores"] = True +为了获取 需要打开 track_scores ? 不一定 +Track scores +When sorting on a field, scores are not computed. By setting track_scores to true, scores will still be computed and tracked. + + + 翻译的health很慢 @@ -67,10 +120,38 @@ translation: +翻译,增加facebook/nllb-200-distilled-600M +https://blog.csdn.net/qq_42746084/article/details/154947534 +https://huggingface.co/facebook/nllb-200-distilled-600M + + +店铺的语言:英语能占到80%,所以专门增加一个en-zh的 +https://huggingface.co/Helsinki-NLP/opus-mt-zh-en +https://huggingface.co/Helsinki-NLP/opus-mt-en-zh + + +opus-mt-zh-en + +from transformers import AutoTokenizer, AutoModelForSeq2SeqLM +model_name = "./models/opus-mt-en-zh" +tokenizer = AutoTokenizer.from_pretrained(model_name) +model = AutoModelForSeq2SeqLM.from_pretrained(model_name) +data = 'test' +encoded = tokenizer([data], return_tensors="pt") +translation = model.generate(**encoded) +result = tokenizer.batch_decode(translation, skip_special_tokens=True)[0] +print(result) + +Qwen3-Reranker-4B-GGUF +https://modelscope.cn/models/dengcao/Qwen3-Reranker-4B-GGUF/summary +1. 要确定选择哪种量化方式 +2. 确定提示词 + + 查看翻译的缓存情况 @@ -114,26 +195,17 @@ HOST:10.200.16.14 / localhost 用户名密码:saas:4hOaLaf41y2VuI8y -你安装过nvidia-container-toolkit吗 -现在有一些开源的推理引擎对向量化模型和重排模型支持的比较好,我们这块也正好要单独拎出来,因此想改造下。 -已决策:embedding 先统一为 Qwen3-Embedding-0.6B(Sentence Transformers,本地 6005),后续若要独立高并发服务优先评估 TEI。 -最好以 docker 方式部署,让 gpu 对 docker 可见需要 nvidia-container-toolkit, -我试了多种方法安装 nvidia-container-toolkit 都失败了 +安装 nvidia-container-toolkit (done) https://mirrors.aliyun.com/github/releases/NVIDIA/nvidia-container-toolkit/ https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/index.html -qwen3-embedding -qwen3-reranker - +qwen3-embedding、qwen3-reranker (done) 选一个推理引擎,相比于我自己直接调 sentence-transformers,主要是多进程和负载均衡、连续批处理,比较有用 当前结论:embedding 场景优先 TEI;vLLM 更偏向生成式与 rerank 场景。 - - - 混用 大模型 使用:hunyuan-turbos-latest 混元 OpenAI 兼容接口相关调用示例:https://cloud.tencent.com/document/product/1729/111007 @@ -144,11 +216,11 @@ hunyuan翻译:使用模型 hunyuan-translation https://cloud.tencent.com/document/product/1729/113395#4.-.E7.A4.BA.E4.BE.8B - 谷歌翻译 基础版:https://docs.cloud.google.com/translate/docs/reference/rest/v2/translate + 阿里云 百炼模型 现在使用的apikey是国内的。 各地域的 Base URL 和对应的 API Key 是绑定的。 diff --git a/docs/工作总结-微服务性能优化与架构.md b/docs/工作总结-微服务性能优化与架构.md new file mode 100644 index 0000000..d45e42e --- /dev/null +++ b/docs/工作总结-微服务性能优化与架构.md @@ -0,0 +1,252 @@ +# 工作总结:微服务性能优化与架构 + +本文档汇总**三个微服务的性能优化**、**架构设计**及**性能测试基线**,便于汇报与后续复现。 + +--- + +## 一、完成三个微服务的性能优化 + +### 1. 文本向量(Embedding) + +**调研与选型**:对 TEI(Text Embeddings Inference)、vLLM 与原有 SentenceTransformers 方案进行对比评估。 + +| 方案 | 特点 | 结论 | +|------|------|------| +| **SentenceTransformers** | 本地 Python 进程,单机推理,无独立服务化 | 此前用于 Qwen3-Embedding-0.6B,扩展性与运维性一般 | +| **vLLM** | 高吞吐推理框架,更适合生成式与重排混合场景 | 纯 embedding 场景通常不作为首选 | +| **TEI** | HuggingFace 官方 embedding 专用推理服务,Docker 部署 | **当前最优选型** | + +**当前方案**:以 **TEI** 为文本向量后端,模型为 `Qwen/Qwen3-Embedding-0.6B`,embedding 服务(端口 **6005**)将 `POST /embed/text` 请求转发至 TEI(默认端口 **8080**)。 + +**具体配置与脚本**: +- **配置**:`config/config.yaml` → `services.embedding.backend: "tei"`,`services.embedding.backends.tei.base_url: "http://127.0.0.1:8080"`、`model_id: "Qwen/Qwen3-Embedding-0.6B"`、`timeout_sec: 20`。 +- **启动**:`scripts/start_tei_service.sh`(Docker 容器);环境变量:`TEI_DEVICE=cuda`(默认)、`TEI_PORT=8080`、`TEI_MODEL_ID=Qwen/Qwen3-Embedding-0.6B`、`TEI_VERSION=1.9`、`TEI_MAX_BATCH_TOKENS=4096`、`TEI_MAX_CLIENT_BATCH_SIZE=24`、`TEI_DTYPE=float16`;T4 自动选镜像 `ghcr.io/huggingface/text-embeddings-inference:turing-1.9`。 +- **编排**:`./scripts/service_ctl.sh start tei` 或 `start embedding`(embedding 会校验 TEI `/health` 后再就绪)。 + +**工程化收益**: +- **独立服务**:TEI 以 Docker 容器运行,与主程序 `.venv` 解耦;embedding 使用 `.venv-embedding`,便于独立扩缩容与升级。 +- **协议统一**:与 `local_st`(SentenceTransformers)可插拔,通过 `config.yaml` 的 `services.embedding.backend` 或环境变量 `EMBEDDING_BACKEND` 切换。 +- **T4 适配**:按 GPU 架构自动选择镜像(T4 用 `turing-*`,Ampere+ 用 `cuda-*`);`TEI_MAX_BATCH_TOKENS`、`TEI_MAX_CLIENT_BATCH_SIZE`、`TEI_DTYPE` 已按短文本场景调优。 +- **健康检查与编排**:`service_ctl.sh` 对 tei 校验容器存在且 `GET http://127.0.0.1:8080/health` 成功;embedding 启动前会等待 TEI 健康。 + +详见:`docs/TEI_SERVICE说明文档.md`、`docs/QUICKSTART.md` §4。 + +--- + +### 2. 重排(Reranker) + +**后端**:vLLM 部署 **Qwen3-Reranker-0.6B**,对外提供 `POST /rerank`(query + docs → scores),端口 **6007**。 + +**针对 T4 的优化(已落地配置)**: +- **精度**:`dtype: "float16"`,降低显存与计算量。 +- **Prefix Caching**:`enable_prefix_caching: true`,对重复前缀(如相同 query)做缓存,减少重复计算。 +- **CUDA 图**:`enforce_eager: false`(默认),利用 vLLM 的 CUDA graph 降低 kernel 启动开销。 +- **按文档长度分批**:`sort_by_doc_length: true`,请求内先按文档长度排序再按 `infer_batch_size` 分批推理,减少 padding 浪费;`length_sort_mode: "char"`(更快,短文本推荐)或 `"token"`(更精确)。 +- **参数搜索结论**:在 T4、1000-doc 口径下对 `infer_batch_size` 做了 24/32/48/64 对比;**单请求延迟(c=1)** 上 `infer_batch_size=64` 最优,故当前默认 `infer_batch_size: 64`;`max_model_len: 256` 满足 query+doc 短文本场景;`gpu_memory_utilization: 0.36` 与 T4 16GB 匹配。 + +**具体配置**(`config/config.yaml` → `services.rerank.backends.qwen3_vllm`): +```yaml +model_name: "Qwen/Qwen3-Reranker-0.6B" +engine: "vllm" +max_model_len: 256 +tensor_parallel_size: 1 +gpu_memory_utilization: 0.36 +dtype: "float16" +enable_prefix_caching: true +enforce_eager: false +infer_batch_size: 64 +sort_by_doc_length: true +length_sort_mode: "char" +instruction: "Given a shopping query, rank product titles by relevance" +``` +环境变量覆盖:`RERANK_BACKEND`、`RERANKER_SERVICE_URL`、`RERANK_VLLM_INFER_BATCH_SIZE`、`RERANK_VLLM_SORT_BY_DOC_LENGTH` 等。启停:`./scripts/service_ctl.sh start reranker`,健康:`curl -sS http://127.0.0.1:6007/health`。 + +详见:`reranker/DEPLOYMENT_AND_TUNING.md`、`reranker/README.md`。 + +--- + +### 3. 图片向量(Image Embedding) + +**方案**:**clip-as-service**(CN-CLIP,ViT-H-14),由独立服务提供图片向量化能力。 + +**具体内容**: +- **端口**:clip-as-service 默认 **51000**(`CNCLIP_PORT`);文本走 TEI(8080),图片走 clip-as-service。 +- **API**:embedding 服务(6005)统一暴露 `POST /embed/text` 与 `POST /embed/image`;图片请求由 `embeddings/server.py` 按配置调用实现 `ImageEncoderProtocol` 的后端(clip-as-service 或本地 CN-CLIP)。 +- **环境与启停**:CN-CLIP 使用独立虚拟环境 `.venv-cnclip`;启动 `scripts/start_cnclip_service.sh`,或 `./scripts/service_ctl.sh start cnclip`;设备可通过 `CNCLIP_DEVICE=cuda`(默认)或 `cpu` 指定。 +- **配置**:图片后端在 `config/config.yaml` 的 `services.embedding` 下配置(若存在 image 相关 backend);clip-as-service 的 flow 配置在 `third-party/clip-as-service/server/torch-flow-temp.yml`。 + +详见:`docs/CNCLIP_SERVICE说明文档.md`、`embeddings/README.md`。 + +--- + +### 4. 翻译(Translation) + +**背景**:原使用 DeepL,后迁移至 **qwen-mt**(如 `qwen-mt-flash`)。qwen-mt 云端限速约 **RPM=60(每分钟 60 请求)**,此前未做大商品量压测,未暴露问题;高并发索引或查询场景下易触限。 + +**当前方案**: +- **迁移至 qwen-flash**:在配置中将翻译改为 **LLM provider + qwen-flash 模型**,由 DashScope 兼容 API 调用,可配置化切换。 +- **可配置化(具体配置)**: + - **入口**:`config/config.yaml` → `services.translation`;`provider: "llm"` 时使用 `providers.llm`,`model: "qwen-flash"`,`timeout_sec: 30`,`base_url` 可选(为空则用 `DASHSCOPE_BASE_URL`);环境变量 `DASHSCOPE_API_KEY` 注入 Key。 + - **Provider 取值**:`provider` 可为 `http`(走翻译服务 6006)、`qwen-mt`(直连 qwen-mt-flush 等)、`deepl`(DeepL API)、`llm`(对话模型 qwen-flash 等);工厂函数 `providers/translation.py` 的 `create_translation_provider(query_config)` 根据 `get_translation_config()` 解析结果返回对应实现。 + - **调用位置**:QueryParser(`query/query_parser.py`)与 Indexer(`indexer/incremental_service.py`、`indexer/indexing_utils.py`)均通过 `create_translation_provider(...)` 获取实例,不写死 URL 或模型名。 +- **缓存**:`services.translation.cache` 支持 `key_prefix: "trans:v2"`、`ttl_seconds`、`sliding_expiration` 等,翻译结果写 Redis,减轻重复请求对限速的影响。 +- **场景支撑**:在线索引(indexer)与 query 请求(QueryParser)共用同一套 provider 配置;可按环境或租户通过修改 `config.yaml` 或环境变量切换 provider/model。 +- **待配合**:**金伟侧对索引侧翻译调用做流量控制**(限流/排队/批量聚合),避免索引高峰打满 qwen 限速,影响在线 query 翻译。 + +--- + +### 5. 内容理解字段(支撑 Suggest) + +**能力**:支持根据商品标题批量生成 **qanchors**(锚文本)、**semantic_attributes**、**tags**,供索引与 suggest 使用。 + +**具体内容**: +- **接口**:`POST /indexer/enrich-content`(Indexer 服务端口 **6004**)。请求体为 `items` 数组,每项含 `spu_id`、`title`(必填)及可选多语言标题等;单次请求最多 **50 条**,建议批量调用。响应 `results` 与 `items` 一一对应,每项含 `spu_id`、`qanchors`(按语言键,如 `qanchors.zh`、`qanchors.en`,逗号分隔短语)、`semantic_attributes`、`tags`。 +- **索引侧**:微服务组合方式下,调用方先拿不含 qanchors/tags 的 doc,再调用本接口补齐后写入 ES 的 `qanchors.{lang}` 等字段;索引 transformer(`indexer/document_transformer.py`、`indexer/product_annotator.py`)内也可在构建 doc 时调用内容理解逻辑,写入 `qanchors.{lang}`。 +- **Suggest 侧**:`suggestion/builder.py` 从 ES 商品索引读取 `_source: ["id", "spu_id", "title", "qanchors"]`,对 `qanchors.{lang}` 用 `_split_qanchors` 拆成词条,以 `source="qanchor"` 加入候选,排序时 `qanchor` 权重大于纯 title(`add_product("qanchor", ...)`);suggest 配置中 `sources: ["query_log", "qanchor"]` 表示候选来源包含 qanchor。 +- **实现与依赖**:内容理解内部使用大模型(需 `DASHSCOPE_API_KEY`),支持多语言与 Redis 缓存(如 `product_anchors`);逻辑与 `indexer/product_annotator` 一致。 + +**状态**:内容理解字段已接入索引与 suggest 链路;依赖内容理解(qanchors/tags)的**全量数据尚未全部完成一轮**,后续需持续跑满并校验效果。 + +详见:`indexer/ANCHORS_AND_SEMANTIC_ATTRIBUTES.md`、`docs/搜索API对接指南.md`(内容理解接口)、`api/routes/indexer.py`(enrich-content 路由)。 + +--- + +## 二、架构 + +### 1. Provider 与动态选择翻译 + +- **设计**:参考 `docs/系统设计文档.md`、`docs/DEVELOPER_GUIDE.md`,翻译/向量/重排均采用 **Provider + Backend** 解耦;配置单一来源为 `config/config.yaml` 的 `services` 块,环境变量可覆盖。 +- **翻译(具体实现)**: + - **工厂**:`providers/translation.py` 的 `create_translation_provider(query_config)`;内部调用 `config/services_config.get_translation_config()` 得到 `provider` 与 `providers.` 参数。 + - **分支**:`provider in ("qwen-mt", "direct", "local", "inprocess")` → 使用 `query/qwen_mt_translate.py` 的 `Translator`(model 如 qwen-mt-flush);`provider == "http"` 或 `"service"` → `HttpTranslationProvider`(base_url 为翻译服务 6006,model 如 qwen);`provider == "llm"` → `query/llm_translate.py` 的 `LLMTranslatorProvider`(model 如 qwen-flash,base_url 可选);`provider == "deepl"` → `query/deepl_provider.py` 的 `DeepLProvider`。 + - **调用方**:`query/query_parser.py`(搜索前翻译)、`indexer/incremental_service.py`、`indexer/indexing_utils.py`(索引时翻译)均通过上述工厂获取实例,不写死 URL 或模型名。 +- **效果**:仅改 `config.yaml` 的 `services.translation.provider` 及对应 `providers.` 即可切换 DeepL、qwen-mt、qwen-flash(llm)、HTTP 翻译服务等。 + +### 2. 服务的监控与拉起机制 + +- **脚本**:`scripts/service_ctl.sh` 统一负责各服务的生命周期与监控;依赖 `scripts/lib/load_env.sh` 与项目根目录 `.env`。 +- **服务与端口**: + - 核心:backend **6002**、indexer **6004**、frontend **6003**。 + - 可选:embedding **6005**、translator **6006**、reranker **6007**、tei **8080**、cnclip **51000**。 + - 端口可由环境变量覆盖:`API_PORT`、`INDEXER_PORT`、`FRONTEND_PORT`、`EMBEDDING_PORT`、`TRANSLATION_PORT`、`RERANKER_PORT`、`TEI_PORT`、`CNCLIP_PORT`。 +- **命令**: + - `./scripts/service_ctl.sh start [service...]` 或 `start all`(all = tei cnclip embedding translator reranker backend indexer frontend,按依赖顺序);`stop`、`restart` 同参数;`status` 默认列出所有服务。 + - 启动时:backend/indexer/frontend/embedding/translator/reranker 会写 pid 到 `logs/.pid`,并执行 `wait_for_health`(GET `http://127.0.0.1:/health`);reranker 健康重试 90 次,其余 30 次;TEI 校验 Docker 容器存在且 `/health` 成功;cnclip 无 HTTP 健康则仅校验进程/端口。 +- **监控常驻**: + - `./scripts/service_ctl.sh monitor-start ` 启动后台监控进程,将 targets 写入 `logs/service-monitor.targets`,pid 写入 `logs/service-monitor.pid`,日志追加到 `logs/service-monitor.log`。 + - 轮询间隔 `MONITOR_INTERVAL_SEC` 默认 **10** 秒;连续 **3** 次(`MONITOR_FAIL_THRESHOLD`)健康失败则触发重启;重启冷却 `MONITOR_RESTART_COOLDOWN_SEC` 默认 **30** 秒;每小时最多重启 `MONITOR_MAX_RESTARTS_PER_HOUR` 默认 **6** 次;超限时调用 `scripts/wechat_alert.py` 告警(若存在)。 +- **日志**:各服务按日滚动到 `logs/-.log`,通过 `scripts/daily_log_router.sh` 与 `LOG_RETENTION_DAYS`(默认 30)控制保留。 + +详见:`scripts/service_ctl.sh` 内注释及 `docs/Usage-Guide.md`。 + +### 3. Suggest 索引与增量 + +- **当前**:suggest 索引由**全量脚本触发**,入口为 `main.py build-suggestions`,或封装脚本 `scripts/build_suggestions.sh [args...]`。 + - **全量示例**:`./scripts/build_suggestions.sh 162 --mode full --days 30 --publish-alias`(重建建议索引,近 30 天数据,并发布别名)。 + - **增量示例**:`./scripts/build_suggestions.sh 162 --mode incremental --overlap-minutes 30`(按 watermark 增量更新);脚本内部调用 `main.py build-suggestions --tenant-id ...`。 + - 构建逻辑在 `suggestion/builder.py` 的 `SuggestionIndexBuilder`:从 ES 商品索引(含 `title`、`qanchors`)与查询日志等拉取数据,写入 versioned 建议索引并切换 alias。 +- **尚未完成的“增量机制”**:指**自动/事件驱动的增量**(如商品变更或日志写入时自动刷新建议索引);当前 incremental 模式为“按 watermark 再跑一次构建”,仍为脚本主动触发,非持续增量流水线。 +- **依赖**:suggest 候选依赖商品侧 **内容理解字段**(qanchors/tags);`sources: ["query_log", "qanchor"]` 表示候选来自查询日志与 qanchor;当前内容理解未全量跑完一轮,suggest 数据会随全量重建逐步完善。 + +详见:`suggestion/builder.py`、`suggestion/ARCHITECTURE_V2.md`、`main.py`(build-suggestions 子命令)。 + +--- + +## 三、性能测试报告摘要 + +以下数据来自 `docs/性能测试报告.md`,测试时间 **2026-03-12**,环境:**8 vCPU**(Intel Xeon Platinum 8255C @ 2.50GHz)、**约 15Gi 可用内存**;租户 **162** 文档数约 **53**(search/suggest/rerank 与文档规模相关)。压测工具:`scripts/perf_api_benchmark.py`,场景×并发矩阵,每档 **20s**。 + +**复现命令(四场景×四并发)**: +```bash +cd /data/saas-search +.venv/bin/python scripts/perf_api_benchmark.py \ + --scenario backend_search,backend_suggest,embed_text,rerank \ + --concurrency-list 1,5,10,20 \ + --duration 20 \ + --tenant-id 162 \ + --backend-base http://127.0.0.1:6002 \ + --embedding-base http://127.0.0.1:6005 \ + --translator-base http://127.0.0.1:6006 \ + --reranker-base http://127.0.0.1:6007 \ + --output perf_reports/2026-03-12/perf_matrix_report.json +``` +执行前需启动服务:`./scripts/service_ctl.sh start embedding translator reranker backend`,并通过各端口 `/health` 检查。Reranker 单独 386-docs 口径复现见性能测试报告 §7.4 与 §13。 + +### 3.1 向量化服务(embed_text) + +| 并发 | 请求数 | 成功率 | 吞吐(RPS) | Avg(ms) | P95(ms) | Max(ms) | +|-----:|-------:|-------:|----------:|--------:|--------:|--------:| +| 1 | 966 | 100% | 48.27 | 20.63 | 23.41 | 49.80 | +| 5 | 1796 | 100% | 89.57 | 55.55 | 69.62 | 109.84 | +| 10 | 2095 | 100% | 104.42 | 95.22 | 117.66 | 152.48 | +| 20 | 2393 | 100% | 118.70 | 167.37 | 212.21 | 318.70 | + +**结论**:随并发提升吞吐持续增长,延迟平滑上升,扩展性较好。 + +--- + +### 3.2 Reranker(rerank) + +口径:query 固定 `wireless mouse`,每次请求 **386 docs**,句长 15–40 词随机(从 1000 词池采样);配置 `rerank_window=384`。复现命令: +```bash +.venv/bin/python scripts/perf_api_benchmark.py \ + --scenario rerank --duration 20 --concurrency-list 1,5,10,20 --timeout 60 \ + --rerank-dynamic-docs --rerank-doc-count 386 --rerank-vocab-size 1000 \ + --rerank-sentence-min-words 15 --rerank-sentence-max-words 40 \ + --rerank-query "wireless mouse" --rerank-seed 20260312 \ + --reranker-base http://127.0.0.1:6007 \ + --output perf_reports/2026-03-12/rerank_realistic/rerank_386docs.json +``` + +| 并发 | 请求数 | 成功率 | 吞吐(RPS) | Avg(ms) | P95(ms) | Max(ms) | +|-----:|-------:|-------:|----------:|--------:|--------:|--------:| +| 1 | 14 | 100% | 0.67 | 1498.64 | 1799.25 | 2160.96 | +| 5 | 15 | 100% | 0.62 | 8011.99 | 9725.61 | 9726.02 | +| 10 | 20 | 100% | 0.61 | 16217.12| 18043.05| 18050.04| +| 20 | 20 | 100% | 0.60 | 33252.35| 33456.74| 33480.14| + +**结论**:在 386 docs/请求 的真实口径下,吞吐约 **0.6 rps**,延迟随并发明显上升(并发 20 时 P95 约 33.5s),是当前整条链路的**最重瓶颈**。与 DashScope 云后端对比:单并发 vLLM 延迟更优;并发 5+ 时云后端吞吐更高(约 1.3x–6x),在线高并发场景可优先考虑云后端。 + +--- + +### 3.3 搜索(backend_search) + +| 并发 | 请求数 | 成功率 | 吞吐(RPS) | Avg(ms) | P95(ms) | Max(ms) | +|-----:|-------:|-------:|----------:|--------:|--------:|--------:| +| 1 | 160 | 100% | 7.98 | 124.89 | 228.06 | 345.49 | +| 5 | 161 | 100% | 7.89 | 628.91 | 1271.49| 1441.02| +| 10 | 181 | 100% | 8.78 | 1129.23| 1295.88| 1330.96| +| 20 | 161 | 100% | 7.63 | 2594.00| 4706.44| 4783.05| + +**结论**:吞吐约 **8 rps** 平台化,延迟随并发上升明显,符合“检索 + 向量 + 重排”重链路特征。多租户补测(文档数 500–10000,见报告 §12)表明:文档数越大,RPS 下降、延迟升高;tenant 0(10000 doc)在并发 20 出现部分 ReadTimeout(成功率 59.02%),需注意 timeout 与容量规划;补测命令示例:`for t in 0 1 2 3 4; do .venv/bin/python scripts/perf_api_benchmark.py --scenario backend_search --concurrency-list 1,5,10,20 --duration 20 --tenant-id $t --output perf_reports/2026-03-12/search_tenant_matrix/tenant_${t}.json; done`。 + +--- + +### 3.4 Suggest(backend_suggest) + +| 并发 | 请求数 | 成功率 | 吞吐(RPS) | Avg(ms) | P95(ms) | Max(ms) | +|-----:|-------:|-------:|----------:|--------:|--------:|--------:| +| 1 | 3502 | 100% | 175.09 | 5.68 | 8.70 | 15.98 | +| 5 | 4168 | 100% | 208.10 | 23.93 | 36.93 | 59.53 | +| 10 | 4152 | 100% | 207.25 | 48.05 | 59.45 | 127.20 | +| 20 | 4190 | 100% | 208.99 | 95.20 | 110.74 | 181.37 | + +**结论**:吞吐高且稳定(**约 200+ rps**),对并发友好,与 search/rerank 相比压力较小。 + +--- + +## 四、总结 + +| 维度 | 要点 | +|------|------| +| **Embedding** | TEI 替代 SentenceTransformers/vLLM 作为文本向量后端,兼顾性能与工程化(Docker、配置化、T4 调优);图片向量由 clip-as-service 承担。 | +| **Reranker** | vLLM + Qwen3-Reranker-0.6B,针对 T4 做 float16、prefix caching、CUDA 图、按长度分批及 batch/长度参数搜索;高并发场景可选用 DashScope 云后端。 | +| **翻译** | 因 qwen-mt 限速(RPM≈60),迁移至可配置的 qwen-flash 等方案,支撑在线索引与 query;需金伟侧对索引做流量控制。 | +| **内容理解** | 提供 qanchors/tags 等字段生成接口,支撑 suggest 与检索增强;全量一轮尚未完全跑满。 | +| **架构** | Provider 动态选择翻译;service_ctl 统一监控与拉起;suggest 目前全量脚本触发,增量待做。 | +| **性能基线** | 向量化扩展性良好;reranker 为整链瓶颈(386 docs 约 0.6 rps);search 约 8 rps;suggest 约 200+ rps。 | + +**关键文件与复现**: +- 配置:`config/config.yaml`(services、rerank、query_config)、`.env`(端口与 API Key)。 +- 脚本:`scripts/service_ctl.sh`(启停与监控)、`scripts/perf_api_benchmark.py`(压测)、`scripts/build_suggestions.sh`(suggest 构建)。 +- 完整步骤与多租户/rerank 对比见:`docs/性能测试报告.md`。 diff --git a/docs/性能测试报告.md b/docs/性能测试报告.md index efdb2fc..1380d37 100644 --- a/docs/性能测试报告.md +++ b/docs/性能测试报告.md @@ -154,7 +154,7 @@ cd /data/saas-search ### 7.4 Reranker(rerank) -测试方法(本节已按新口径重跑): +测试方法: - `query` 固定为 `wireless mouse` - 每次请求 `docs=386` - 从 `1000` 个候选单词中随机采样,先随机句长 `15-40`,再生成每条 doc 句子 -- libgit2 0.21.2