别再死磕 Elasticsearch 了,这个轻量级搜索引擎更香

0 阅读5分钟

关注我的公众号:【编程朝花夕拾】,可获取首发内容。

01 引言

这段时间一直在做紧急项目,有一个类似朋友圈的功能,需要查询发布的内容。第一印象跟肯定是全文检索了,全文检索最常用的就是Elasticsearch 或者Solr

之前分享过一篇关于Manticore Search的文章,但是一直没有使用。这次就深度使用一下。它比 Elasticsearch 轻量得多,性能却不虚,而且 SQL 支持做得比 ES 好太多。

02 Manticore Search 简介

Manticore Search 是一个高性能、多功能的搜索引擎数据库,专门为搜索和数据分析场景打造。它的前身是 Sphinx Search,2017 年正式独立出来并进行了完全重写。它兼具以下特性:

  • 高性能全文搜索:全文检索性能优异,支持超过 20 种全文运算符和 20+ 排名因子
  • 实时索引:新增或更新的文档可以立即被读取,无需等待定时任务
  • 向量搜索:支持嵌入向量最近邻搜索,可构建语义搜索、相似推荐等能力
  • 列式存储:可选的列式存储,大幅降低大数据集的内存占用
  • 双协议支持:原生支持 SQL(MySQL 协议/HTTP SQL)和 HTTP JSON 协议
  • 水平扩展:支持Galera多主复制,可横向扩展

Manticore Search 开源免费,核心由 C++ 编写,启动极快,内存占用极低(空实例仅约 40MB RSS)。这玩意儿在国内真的被 Elasticsearch 压得太惨了,没什么人知道。

03 核心功能

3.1 全文检索

全文搜索的关键词是match,输入查询字符串使用与索引文本时相同的设置进行分词。除了输入文本的分词外,查询字符串还支持一系列的 全文操作符,这些操作符会强制执行各种规则,以确保关键词能够提供有效的匹配。

全文匹配子句可以与属性 过滤器 结合使用,作为 AND 布尔运算。全文匹配和属性过滤器之间的 OR 关系不被支持。

SELECT 子句中最多只能有一个 MATCH()。还可以支持JOIN查询。

主要的功能如下:

  • 模糊搜索(Fuzzy Search):自动纠正用户拼写错误
  • 自动补全(Autocomplete):支持前缀和短语补全
  • 同义词(Synonyms)词形还原(Stemming/Lemmatization)
  • 中文分词:支持准确的中文分割
  • 高亮(Highlighting):搜索结果关键词高亮
  • 自定义排名:支持自定义排序规则

3.2 向量搜索

可以将机器学习模型生成的向量嵌入添加到文档中,执行最近邻(KNN)搜索,适用于:

  • 语义搜索
  • 相似内容推荐
  • 图片、视频、音频的相似性搜索

3.3 结构化查询

除了全文搜索,Manticore Search 还支持:

  • 范围过滤(Range):如 id < 100
  • 布尔过滤(BoolFilter):组合多个条件(must/must_not/should)
  • JOIN 查询:跨表关联查询
  • 聚合统计:faceted search 等

3.4 存储模式

Manticore 提供行存储和列存储选项,以适应不同规模的数据集。行存储是大家最常用的存储模式,如Mysql等,而列式存储被称之为newSQL,如StarrocksClickHourseTiDB等分布式数据库。

04 基于Match的实践

Manticore的部署非常简单,直接使用Docker部署即可。

docker pull manticoresearch/manticore
docker run --name manticore -p9306:9306 -p9308:9308 -p9312:9312 -d manticoresearch/manticore

4.1 准备工作

我们以Java客户端为例,因为对应的依赖:

<dependency>
    <groupId>com.manticoresearch</groupId>
    <artifactId>manticoresearch</artifactId>
    <version>8.1.0</version>
    <scope>compile</scope>
</dependency>

客户端:

ApiClient apiClient = Configuration.getDefaultApiClient();
apiClient.setBasePath("http://ip:9308");

所有 API 操作都通过 ApiClient 发起,9308Manticore Search 的默认 HTTP 端口(SQLJSON 协议共用)。

造数据。使用AI造了10W条数据:

4.2 SQL 查询

直接使用SQL查询。

ApiClient apiClient = Configuration.getDefaultApiClient();
apiClient.setBasePath("http://127.0.0.1:9308");

UtilsApi utilsApi = new UtilsApi(apiClient);

String sql = """
        select * from blogs where match('Docker') and id < 100
        """;

SqlResponse response = utilsApi.sql(sql, true);
Object actualInstance = response.getActualInstance();
// 解析json数据
String json = JSON.toJSONString(actualInstance);
List<JSONObject> jsonObjects = JSON.parseArray(json, JSONObject.class);
String data = jsonObjects.get(0).getString("data");
List<JSONObject> dataList = JSON.parseArray(data, JSONObject.class);
for (JSONObject jsonObject : dataList) {
    System.out.println(jsonObject);
}

解析:

  • 使用 UtilsApi.sql() 执行原始 SQL 语句
  • match('Docker') 是 Manticore Search 的全文搜索语法,表示在全文索引字段中搜索包含 "Docker" 的文档且ID小于100的记录
  • 使用JSON解析最终的记录

结果:

图像化界面结果:

4.3 SearchApi

除了使用SQL,还可以使用API的方式直接调用,有点类似ESAPI

ApiClient apiClient = Configuration.getDefaultApiClient();
apiClient.setBasePath("http://127.0.0.1:9308");
SearchApi searchApi = new SearchApi(apiClient);

SearchQuery searchQuery = new SearchQuery();

Map<String, Object> match = new HashMap<>();
match.put("content", "Docker");

Map<String, Object> rangeItem = new HashMap<>();
rangeItem.put("lt", 100);

Map<String, Object> range = new HashMap<>();
range.put("id", rangeItem);

BoolFilter boolFilter = new BoolFilter();
QueryFilter queryFilter = new QueryFilter();
queryFilter.setMatch(match);
boolFilter.addMustItem(queryFilter);
QueryFilter queryFilter2 = new QueryFilter();
queryFilter2.setRange(range);
boolFilter.addMustItem(queryFilter2);
searchQuery.setBool(boolFilter);


SearchRequest searchRequest = new SearchRequest();
searchRequest.setTable("blogs");
searchRequest.setQuery(searchQuery);

SearchResponse response = searchApi.search(searchRequest);
System.out.println("总命中: " + response.getHits().getTotal());
System.out.println("list: " + JSON.toJSONString(response.getHits().getHits()));

解析:

  • 这该示例展示了如何组合多个过滤条件
  • BoolFilter 支持四种逻辑组合:
    • must:所有条件都必须满足(AND)
    • must_not:所有条件都不能满足(NOT)
    • should:满足任一条件即可(OR)
    • filter:同 must,但不参与评分
  • 本例中 addMustItem() 添加了两个必须同时满足的条件:
    1. content 字段匹配 "Docker"
    2. id 字段小于 100

结果:

其中结果集里面包含了_score,表示命中的椎确分数。

类似ESAPI:

curl -sX POST http://localhost:9308/search  -d '
{
  "table":"blogs",
  "query": {
    "bool": {
      "must": [
        { "match": {"content":"Docker"} },
        { "range": { "id": { "lt": 100 } } }
      ]
    }
  }
}

4.4 Mysql和Manticore的速度

我们可以看一下普通的like查询和Manticorematch的速度对比,同样10W条数据。

Mysql的查询结果:

差不多耗时:142ms

Manticore结果:

差不多耗时:14ms

两者相差有10倍。

05 小结

Manticore Search的客户端可以使用DBeaver,之前的版本是不能使用的,打开直接报错,主要因为别名的问题。后面已经修复。相关的issues

github.com/manticoreso…

Manticore Search 是一个被严重低估的搜索引擎,国内用户依然还是首选ES,如果你也在寻找替代方案,不妨了解一下这个产品。