# KupujemProdajem 如何使用 Manticore Search 以约 10 毫秒检索 560 万条广告

塞尔维亚最大的在线分类信息市场如何使用 Manticore Search 以约 10 毫秒检索 560 万条在售广告、处理实时更新，并为混合搜索做好准备。

对于在线市场来说，拥有数百万条商品信息只有在买家能找到合适的那一条时才有价值。

在 [KupujemProdajem](https://www.kupujemprodajem.com/)，塞尔维亚最大的在线分类信息市场里，搜索是用户发现广告的主要方式。买家会通过网页和移动应用搜索，内部团队则用搜索进行审核和支持。因此，相关性和延迟都会直接影响用户参与度，以及卖家被发现的机会。

如今，KupujemProdajem 使用 Manticore Search 检索大约 **560 万条在售广告**，而独立的管理索引则包含约 **7000 万行**当前和历史数据。生产集群平均每秒处理约 **150 个搜索请求**，Manticore 的平均查询时间约为 **10 毫秒**。

而同一套搜索基础设施，现在也在为团队打开一条通往更雄心勃勃目标的路径：专为塞尔维亚语市场内容设计的 [混合词法与语义搜索](/blog/hybrid-search/)。

## 搜索是核心产品的一部分

KupujemProdajem 维护两类主要搜索索引。第一类支撑面向用户的体验。它包含大约 560 万条在售分类广告，包括标题、描述、类别和结构化属性。用户在 Web 和移动端浏览市场时，会对这些索引进行搜索。

第二组索引用于内部。除当前数据外，这些索引还保留了部分已删除广告的历史记录，使审核和支持团队能够搜索那些在公开市场上可能已经不再可见的信息。

这让搜索在产品的两端都变得重要。对买家来说，它决定了他们从“想找什么”到找到相关列表的速度。对卖家来说，搜索影响他们的广告能否被发现。对内部团队来说，它让员工在排查问题时能够访问历史市场数据。

正如 KupujemProdajem 的后端开发者 [Dimitrije Marinković](https://www.linkedin.com/in/dimitrije-marinkovi%C4%87-131bb62bb/) 所说：

> “Search 是平台上的主要发现机制，因此相关性和延迟会直接影响用户参与度和卖家成功。”

## 选择一款能够随产品一起成长的搜索引擎

KupujemProdajem 多年来一直在运行专用搜索引擎。当其之前的引擎 Sphinx 不再积极开发时，团队需要一个仍在维护、且能适配现有架构的替代方案，同时又不必重建整套搜索集成。

但兼容性只是决策的一部分。新引擎还需要支持：

- 持续保持较低的查询延迟；
- 面向数百万广告的 [实时变更](https://manual.manticoresearch.com/Creating_a_table/Local_tables/Real-time_table)；
- [复制](https://manual.manticoresearch.com/Creating_a_cluster/Setting_up_replication/Setting_up_replication)；
- 能与团队以 MySQL 为中心的 PHP 技术栈自然协作的 [SQL](https://manual.manticoresearch.com/Connecting_to_the_server/MySQL_protocol)；
- [全文搜索](https://manual.manticoresearch.com/Searching/Full_text_matching/Basic_usage) 以及 [结构化过滤](https://manual.manticoresearch.com/Searching/Filters)；
- 灵活的 [相关性调优](https://manual.manticoresearch.com/Searching/Options#ranker)；
- 以及迈向 [向量](/blog/vector-search-deep-dive/) 和 [混合搜索](https://manual.manticoresearch.com/Searching/Hybrid_search) 的路径。

Manticore 在一个系统里提供了这些能力。最后这一点要求，正变得越来越重要。

KupujemProdajem 计划使用自己的 [嵌入模型](/blog/vector-search-in-databases/#embeddings)，把传统词法搜索与向量搜索结合起来。由于 Manticore 支持与全文搜索并行的 [KNN 搜索](https://manual.manticoresearch.com/Searching/KNN)，团队可以直接在现有的搜索基础设施中构建这项能力，而不必把数据同步到单独的向量数据库。

## 让数百万条持续变化的广告实时可搜索

MySQL 仍然是市场数据的事实来源。每当广告被创建、编辑或删除时，相应变更都会实时推送到 Manticore。生产环境的负载平均约为每秒 **40 次 [`REPLACE`](https://manual.manticoresearch.com/Data_creation_and_modification/Updating_documents/REPLACE) 操作**，外加 [`UPDATE`](https://manual.manticoresearch.com/Data_creation_and_modification/Updating_documents/UPDATE) 和 [`DELETE`](https://manual.manticoresearch.com/Data_creation_and_modification/Deleting_documents) 操作。

这对分类信息场景尤其有用。市场库存在持续变化。卖家不断发布新广告、更新价格和描述、修改状态，并移除不再可售的商品。

因此，搜索系统需要同时完成两项工作：

1. 快速返回相关结果；
2. 确保这些结果反映当前的市场状态。

KupujemProdajem 使用 Manticore 的实时更新，让市场的可搜索表示在这些变化发生时与 MySQL 保持同步。

## 不只是匹配词语

市场搜索与检索一堆普通文档有很大不同。一个列表既包含文本，也包含分类、价格、地点、状态和其他属性等结构化信息。KupujemProdajem 在 Manticore 中将全文匹配与这些类型化属性上的过滤结合起来。

团队还会控制列表中不同部分对相关性的影响。例如，标题的权重会明显高于描述或分类。这对分类广告来说非常直观：标题里直接写出的商品名，通常比长描述中某个位置出现的同一个词更能说明相关性。

在此基础上，KupujemProdajem 使用了一个针对其相关性需求调优的 [自定义排序器](https://manual.manticoresearch.com/Searching/Sorting_and_ranking#Ranking-overview)，并按广告对结果进行 [分组](https://manual.manticoresearch.com/Searching/Grouping)。这让团队能够编码市场特定的相关性规则，而不是完全依赖通用排序公式。

## 一个小型复制集群

整个 Manticore 部署运行在四台虚拟机上。三台节点组成生产复制集群，第四台 VM 作为备份。所有写入都会发送到一个节点，然后复制到其他节点。读取请求则由应用层逻辑分配到全部三台生产节点。

架构相对简单：**MySQL → 实时更新 → Manticore 写入节点 → 复制的 Manticore 节点 → 网页/移动端/管理端搜索**

这种简单性在运维上很重要。团队不需要为了支撑市场搜索而引入大型分布式数据平台。同一个集群即可处理数据摄取、全文检索、结构化过滤、自定义排序、复制，以及未来向量搜索的基础能力。

## 560 万条在售广告，7000 万条管理行

面向用户的索引目前包含约 **560 万条在售广告**，占用约 **9 GB**。规模更大的管理索引包含约 **7000 万行**，其中包括部分历史已删除广告数据，占用约 **90 GB**。

在三节点集群上，KupujemProdajem 观察到大致如下：

| 指标 | 生产规模 |
|---|---:|
| 在售广告 | ~560万 |
| 管理索引行数 | ~7000 万 |
| 面向用户的索引大小 | ~9 GB |
| 管理索引大小 | ~90 GB |
| 搜索流量 | 平均 ~150 次查询/秒 |
| 每节点搜索流量 | 平均 ~50 次查询/秒 |
| `REPLACE` 流量 | ~40 次/秒 |
| 平均查询时间 | ~10 ms |
| 基础设施 | 3 台生产 VM + 1 台备份 |

集群在正常运行下保持稳定，没有卡住或长时间运行的查询。偶尔会出现约 150 ms 的短暂延迟峰值，而平均查询执行时间保持在约 10 ms。之所以这些数字特别有价值，是因为这并不是合成基准测试，而是支撑大型市场主要发现体验的真实生产负载。

## 这套架构为用户带来了什么

基础设施指标很重要，但真正重要的是它们在产品层面带来了什么。对买家而言，Manticore 帮助 KupujemProdajem 在查询数百万条列表、应用相关性逻辑和结构化过滤时仍保持搜索快速。由于变更会持续推入索引，搜索也能快速反映市场库存变化，而不必依赖偶发的大规模重建索引任务。

对卖家来说，这套基础设施支撑着买家发现其列表的主要机制。对审核和支持团队来说，更大的内部索引让历史广告信息即使在不再属于实时市场时，仍然可以被搜索。

## 下一项挑战：理解塞尔维亚语意图

KupujemProdajem 团队下一个项目是 [混合搜索](/blog/hybrid-search/)。当用户输入的词也出现在广告中时，传统全文搜索尤其有效。但市场用户和卖家并不总是会用完全相同的方式描述同一件事。

这个问题在塞尔维亚语里会更有意思。团队指出了几个挑战：

- 形态变化丰富的 [morphology](https://manual.manticoresearch.com/Creating_a_table/NLP_and_tokenization/Morphology)；
- 同时使用拉丁字母和西里尔字母；
- [同义词](https://manual.manticoresearch.com/Creating_a_table/NLP_and_tokenization/Wordforms)；
- 买家和卖家表达相同意图的不同方式。

因此，KupujemProdajem 正在为塞尔维亚市场内容准备自己的嵌入模型。计划是在查询时同时使用 [词法匹配](/use-case/lexical-search/) 和 KNN 向量搜索检索结果，然后把两组信号结合起来。

用户收益很直接。有人可能会用卖家列表里根本没有字面出现的词来搜索某个商品，但该列表实际上高度相关。语义检索可以提供另一种信号，帮助识别这种关系。

正如 Dimitrije 对预期结果的描述：

> “语义匹配会让用户即使输入的措辞与商品信息并不完全一致，也能找到相关广告。”

对工程团队来说，也许同样重要的是，他们无需为此再引入一套数据库。向量表示可以与已经能在 Manticore 中搜索的文本和结构化属性并存，让团队在同一个引擎里结合词法相关性、过滤和语义相似度。

## 更好的市场搜索基础

KupujemProdajem 已经在其市场中最重要的工作流之一上使用 Manticore。大约 **560 万条在售广告**可被检索，平均引擎查询时间约为 **10 毫秒**。更新持续不断到达。复制机制提供了多个搜索节点。内部团队还使用同样的技术检索更大的历史数据集。

但更有意思的部分可能是接下来会发生什么。团队现在可以在不替换搜索基础设施、也不额外运营向量数据库的情况下，试验语义检索和混合相关性。

对 KupujemProdajem 来说，这意味着 Manticore 不只是让今天的市场搜索保持快速，它也在为团队提供一种切实可行的方式，让明天的搜索更好。
