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

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

01 引言

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

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

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部署即可。

shell 复制代码
docker pull manticoresearch/manticore
docker run --name manticore -p9306:9306 -p9308:9308 -p9312:9312 -d manticoresearch/manticore

4.1 准备工作

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

xml 复制代码
<dependency>
    <groupId>com.manticoresearch</groupId>
    <artifactId>manticoresearch</artifactId>
    <version>8.1.0</version>
    <scope>compile</scope>
</dependency>

客户端:

java 复制代码
ApiClient apiClient = Configuration.getDefaultApiClient();
apiClient.setBasePath("http://ip:9308");

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

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

4.2 SQL 查询

直接使用SQL查询。

java 复制代码
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

java 复制代码
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:

bash 复制代码
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,如果你也在寻找替代方案,不妨了解一下这个产品。

相关推荐
葫芦和十三4 小时前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp4 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑5 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯6 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan8 小时前
多Agent之间的区别
后端
青石路9 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充10 小时前
1.面向对象设计思想
后端
IT_陈寒10 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro11 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
要阿尔卑斯吗11 小时前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端