Elasticsearch的倒排索引与分词器原理
要真正用好Elasticsearch,必须理解其核心数据结构——倒排索引,以及倒排索引构建的前提——分词器。本文将拆解这两个核心概念的工作原理,并展示它们如何协同工作,让你能高效地进行全文搜索。
理解倒排索引:从“图书目录”说起
想象一本没有目录的厚书。要找“分布式锁”这个词,你只能从第一页翻到最后一页,逐页查找。这就是传统的“正向索引”思路:知道文档,查找内容。效率极低。
Elasticsearch使用的“倒排索引”思路恰恰相反。就像一本书的索引页:
- 收集 书中的所有关键术语(如“分布式锁”、“Redis”、“一致性”)。
- 记录 每个术语出现在哪些页码(文档ID)。
- 当你查“分布式锁”时,直接查阅索引页,立刻得到所有相关页码。
这就是“倒排”:知道内容(词项),查找文档。
我们通过一个表格来直观对比:
| 索引类型 | 工作方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 正向索引 | 文档ID -> 文档内容 |
直观,容易获取完整文档 | 查找特定词项需遍历所有文档,速度慢 | 关系型数据库主键查询 |
| 倒排索引 | 词项 -> [文档ID列表] |
查找包含特定词项的文档极快 | 建立索引需要额外存储和计算,更新复杂 | 全文搜索,搜索引擎核心 |
倒排索引的创建过程
当你向Elasticsearch索引(index)一个文档时,背后发生了以下关键步骤:
-
提交文档。你发送一个JSON格式的文档,例如:
{ "title": "Elasticsearch的倒排索引与分词器原理", "content": "本文讲解倒排索引和分词器是如何工作的。" } -
字段分析。Elasticsearch会根据字段映射(
mapping)中定义的分析器(analyzer)来处理每个字段的文本内容。对于title和content这样的全文字段,默认会使用standard分析器。 -
分词与词项处理。分析器内部会执行一系列操作:
- 字符过滤:首先对原始文本进行预处理,比如去掉HTML标签。
- 分词:将文本切分成一系列独立的“词项”(
term)。例如,“Elasticsearch的倒排索引”可能会被切成[“elasticsearch”, “的”, “倒排”, “索引”]。 - 词项过滤:对分词结果进行优化,例如全部转为小写、去除停用词(如“的”、“是”)、同义词扩展、词干提取(将“running”变成“run”)。
-
构建倒排索引。处理后的词项列表及其元数据被写入到倒排索引中。核心结构包含两部分:
- 词项字典:一个排序后的所有唯一词项的列表。
- 倒排列表:对于词项字典中的每个词项,都关联一个“倒排列表”,记录了:
- 包含该词项的所有文档ID。
- 词频:该词项在每个文档中出现的次数(用于相关性计算)。
- 位置:词项在文档中出现的位置(用于短语查询)。
经过这个过程,原始的文档内容就被转化为了高效的搜索数据结构。
深入分词器:搜索的“预处理器”
分词器是倒排索引的建造师。它决定了文档内容和搜索词如何被“理解”和“切分”。如果文档里写的是“分布式锁”,而用户搜索“分布式 锁”,分词器必须保证两者能被匹配上。
一个完整的分析器(analyzer)由三部分组成,像一条流水线:
- 字符过滤器:在分词前预处理文本。
- 分词器:将文本切分成词项。
- 词项过滤器:在分词后处理每个词项。
Elasticsearch内置了多种分析器。standard分析器是默认选择,它适用于大多数西文语言。但处理中文,我们需要专门的中文分词器,如ik_analyzer,它能理解中文词汇边界,避免将“分布式锁”切成单字“分”、“布”、“式”、“锁”。
自定义分析器的步骤
当你需要特殊处理(如搜索拼音、过滤敏感词)时,可以创建自定义分析器。
-
创建索引时定义分析器。在索引的
settings中配置。PUT /my_custom_index { "settings": { "analysis": { "analyzer": { "my_custom_analyzer": { "type": "custom", "char_filter": ["html_strip"], "tokenizer": "ik_max_word", "filter": ["lowercase", "stop"] } } } } }这个名为
my_custom_analyzer的分析器流程是:先 去除HTML标签,然后 使用ik_max_word分词器进行最细粒度分词,最后 转为小写并过滤停用词。 -
在字段映射中使用它。
PUT /my_custom_index/_mapping { "properties": { "content": { "type": "text", "analyzer": "my_custom_analyzer" } } } -
测试分词效果。使用
_analyzeAPI,这是调试分词器的神器。POST _analyze { "analyzer": "my_custom_analyzer", "text": "本文讲解 <b>Elasticsearch</b> 的倒排索引。" }执行后,你会看到文本被正确切分成一个个词项,
<b>和</b>标签已被移除。
搜索流程:两者如何协同工作
现在,让我们看看一个搜索请求是如何利用倒排索引和分词器的。
假设你执行了以下搜索:
GET /my_custom_index/_search
{
"query": {
"match": {
"content": "分布式锁原理"
}
}
}
背后的执行流程:
-
查询词分析。Elasticsearch应用相同的分析器(
my_custom_analyzer)来处理查询词“分布式锁原理”。假设ik_max_word将其切分为[“分布式”, “锁”, “原理”]三个词项。 -
倒排索引查找。Elasticsearch拿着这三个词项,去倒排索引中查找:
- 查找 “分布式” 在哪些文档中出现。
- 查找 “锁” 在哪些文档中出现。
- 查找 “原理” 在哪些文档中出现。
-
合并与评分。合并这些文档ID列表,找出同时包含多个词项的文档(这涉及复杂的布尔逻辑)。然后,根据词频、逆文档频率等因子,计算每个文档的相关性得分(
_score)。 -
返回结果。返回得分最高的文档列表。
关键点在于:索引时使用的分析器和搜索时使用的分析器必须保持一致,或者至少搜索分析器的处理结果要是索引分析器的子集。否则,就会出现“文档里有,但搜不到”的尴尬情况。
总结与最佳实践
掌握倒排索引和分词器,能让你在设计和调优Elasticsearch时做出正确决策。
- 为字段选择正确的分析器。
keyword类型(用于精确匹配、过滤、聚合)和text类型(用于全文搜索)是根本区别。对全文字段,务必在mapping中显式指定适合你语言的分析器。 - 善用
_analyzeAPI。任何对分词结果的疑问,都可以用它来验证。确保文档内容和查询词被处理成你期望的样子。 - 理解评分机制。相关性得分的核心是词频-逆文档频率(TF-IDF)的变体。词在当前文档出现频率越高(TF),在整个索引中出现的文档越少(IDF),得分就越高。
- 中文场景首选
ik分词器。使用ik_max_word进行索引(最细分词,召回率高),使用ik_smart进行搜索(最粗分词,精确度高),是常见的优化组合。
通过正确配置分词器来构建高质量的倒排索引,是发挥Elasticsearch强大搜索能力的基石。

暂无评论,快来抢沙发吧!