核心概念与倒排索引
1. 这是什么
很多人第一次学 ElasticSearch(后面简称 ES)时,会先记住几个词:
- Index
- Document
- Field
- Term
- Analyzer
- 倒排索引
但背完之后还是会迷糊:
- 为什么 ES 查关键词这么快?
- 为什么改了分词器,查询结果就变了?
- 为什么 ES 的“索引”跟 MySQL 的索引不是一回事?
这篇就先把这些问题讲透。
如果只用一句话解释倒排索引,可以这么记:
普通数据库更像“按记录找内容”,ES 更像“按词找记录”。
比如你有 3 篇文档:
Java 并发编程入门Java 网络编程与 NettyMySQL 索引设计实践
如果用户搜索“Java”,ES 不会一篇篇全文扫描后再判断有没有这个词,而是更像先准备一张表:
Java -> 文档1, 文档2并发 -> 文档1Netty -> 文档2MySQL -> 文档3
这样查“Java”时,直接就能定位到相关文档。
这张“词 -> 文档列表”的结构,就是倒排索引最核心的直觉。
2. 为什么重要
如果不理解倒排索引,后面很多 ES 行为你都会觉得像“黑盒”:
- 为什么
text和keyword查询效果不一样 - 为什么搜索
中国人时结果和预期不同 - 为什么有时候搜
running能命中run - 为什么字段建模不合理会拖慢查询和写入
本章的重要性在于:
- 它是全文检索的底层心智模型
- 它决定了你怎么设计字段映射
- 它直接影响查询精度、召回率和性能
换句话说:
如果不懂倒排索引,你大概率只能“会调 API”,还不会“设计搜索系统”。
3. 先和 MySQL 建立区分
初学者最容易混的地方是:
- ES 里有
Index - MySQL 里也有
index
但这两个“索引”不是一回事。
3.1 MySQL 里的索引更像什么
MySQL 索引更多是:
- 为某个字段建立加速结构
- 帮助快速定位行记录
- 本质上还是围绕“表中的行”组织
3.2 ES 里的 Index 更像什么
ES 的 Index 更接近:
- 一类文档的逻辑集合
- 类似数据库里的“库/表”混合概念
- 里面包含 mapping、settings、documents
所以你可以先粗略理解成:
- MySQL 的 index:给查表加速的结构
- ES 的 Index:存放文档的一整个索引库
这是两个层级不同的概念。
4. 核心概念,先用人话讲清楚
4.1 Document:文档
ES 里一条数据通常叫一个 Document。
例如一篇博客:
{
"title": "Java 网络编程与 Netty",
"author": "yanqi",
"content": "Netty 是一个基于事件驱动的网络编程框架",
"tags": ["Java", "Netty", "网络编程"]
}这就是一个文档。
4.2 Field:字段
文档里的每个键值对就是字段,比如:
titleauthorcontenttags
字段类型会影响:
- 是否分词
- 是否可聚合
- 是否适合排序
4.3 Term:词项
Term 可以理解为:
- 经过分析处理后的最小查询单元
比如文本:
Java 网络编程与 Netty经过分词后,可能变成:
java网络编程netty
这些被拆出来并进入索引结构的词,就是 term。
4.4 Token:分词后的片段
在很多资料里你会同时看到 token 和 term。
可以先这样理解:
token:分词器处理过程中的结果term:最终进入倒排索引的词项
入门阶段不用在这两个概念上过度纠缠,但要知道它们都和“文本被切成可搜索单元”有关。
4.5 Analyzer:分词器
Analyzer 是 ES 全文检索里非常关键的角色。
它通常包含三步:
- 字符过滤(character filter)
- 分词(tokenizer)
- token 过滤(token filter)
你可以简单理解成:
Analyzer 决定了一段文本,会被拆成哪些词。
这也就决定了:
- 能不能搜到
- 搜到了什么
- 搜得准不准
5. 倒排索引到底长什么样
假设有三篇文档:
文档1:Java 并发编程
文档2:Java 网络编程
文档3:MySQL 索引设计如果用最朴素的方式建倒排索引,可以得到:
Java -> [1, 2]
并发 -> [1]
编程 -> [1, 2]
网络 -> [2]
MySQL -> [3]
索引 -> [3]
设计 -> [3]这张表就叫 倒排表。
为什么叫“倒排”?
因为它和“正排”是反过来的:
正排思路
- 文档1 -> 包含哪些词
- 文档2 -> 包含哪些词
- 文档3 -> 包含哪些词
倒排思路
- 词A -> 出现在哪些文档
- 词B -> 出现在哪些文档
- 词C -> 出现在哪些文档
搜索引擎更关心的是:
- 用户搜了一个词
- 这个词在哪些文档里出现
所以倒排索引特别适合关键词检索。
6. 一个通俗例子:为什么 ES 查得快
把倒排索引想成图书馆索引卡片:
- 普通扫描:一本本翻书找“分布式”这个词
- 倒排索引:先去索引卡片里查“分布式”,直接拿到书目编号
这就是 ES 快的核心原因之一。
当然,真实 ES 远比这个复杂,还会涉及:
- 词频
- 位置
- 相关性评分
- segment
- merge
但在入门阶段,先建立上面的直觉最重要。
7. text 和 keyword 到底有什么区别
这是最常见、也最容易踩坑的点。
text
适合:
- 全文检索
- 需要分词的文本
例如:
- 文章标题
- 正文内容
- 商品描述
keyword
适合:
- 精确匹配
- 聚合
- 排序
- 过滤
例如:
- 用户 ID
- 订单状态
- 标签编码
- 国家代码
一个典型误区
把所有字符串都建成 text。
后果是:
- 你明明想做精确过滤
- 却变成了分词查询
- 聚合和排序效果也不理想
一个更常见的设计是多字段:
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
}这样:
title用来全文检索title.keyword用来精确匹配、排序、聚合
8. 动手做一个最小实验
下面直接用 curl 走一遍,你会比只看概念快得多。
假设本地 ES 运行在
localhost:9200
8.1 创建索引
curl -X PUT 'http://localhost:9200/blog_demo' \
-H 'Content-Type: application/json' \
-d '{
"mappings": {
"properties": {
"title": { "type": "text" },
"author": { "type": "keyword" },
"content": { "type": "text" }
}
}
}'8.2 写入两篇文档
curl -X POST 'http://localhost:9200/blog_demo/_doc/1' \
-H 'Content-Type: application/json' \
-d '{
"title": "Java 网络编程与 Netty",
"author": "yanqi",
"content": "Netty 是一个基于事件驱动的高性能网络框架"
}'curl -X POST 'http://localhost:9200/blog_demo/_doc/2' \
-H 'Content-Type: application/json' \
-d '{
"title": "MySQL 索引设计实践",
"author": "yanqi",
"content": "索引设计会直接影响查询性能和写入成本"
}'8.3 搜索 Netty
curl -X GET 'http://localhost:9200/blog_demo/_search' \
-H 'Content-Type: application/json' \
-d '{
"query": {
"match": {
"content": "Netty"
}
}
}'你应该看到第一篇文档被命中。
8.4 精确过滤作者
curl -X GET 'http://localhost:9200/blog_demo/_search' \
-H 'Content-Type: application/json' \
-d '{
"query": {
"term": {
"author": "yanqi"
}
}
}'这里要特别感受:
match更偏全文检索term更偏精确匹配
9. 再做一步:看看分词结果
理解 ES,最实用的一步就是学会看 analyzer 的输出。
curl -X POST 'http://localhost:9200/_analyze' \
-H 'Content-Type: application/json' \
-d '{
"analyzer": "standard",
"text": "Java Netty 入门实践"
}'你会得到类似:
{
"tokens": [
{ "token": "java" },
{ "token": "netty" },
{ "token": "入门实践" }
]
}这一步非常关键,因为很多“为什么搜不到”的问题,本质上就是:
- 你以为分成 A、B、C
- 实际 analyzer 分出来的是 X、Y、Z
所以排查 ES 查询问题,第一反应常常应该是:
先看 mapping,再看 analyzer 输出。
10. 相关性为什么不是“只要包含就一样”
很多人会问:
- 都包含关键词,为什么排序不一样?
因为 ES 不只是判断“有没有”,还会计算“相关程度”。
影响相关性的常见因素包括:
- 词出现次数
- 字段权重
- 是否完整匹配
- 查询语句类型
例如:
- 标题里有
Netty - 正文里也有
Netty
通常标题命中的权重会更高。
这也是为什么搜索系统不是“查到了就结束”,还要管“排得对不对”。
11. 实战里最容易踩的坑
11.1 把 ES 当成关系数据库替代品
ES 很擅长:
- 搜索
- 聚合分析
- 海量文档检索
但它不是为了替代关系数据库的强事务能力而设计的。
11.2 字段类型建错
典型错误:
- 需要精确匹配的字段用了
text - 需要全文检索的字段用了
keyword
结果就是:
- 搜索不准
- 聚合不对
- 排序怪异
11.3 不验证分词结果,只盲目改查询 DSL
很多查询问题不是 DSL 写错,而是:
- mapping 不合理
- analyzer 不合理
- 数据入库方式和查询方式不匹配
11.4 用 term 查 text 字段
这是新手高频错误。
text 字段通常会先分词后再建立索引,直接 term 查询时,往往和你想象的不一样。
12. 一套排查思路
以后你遇到“为什么搜不到/搜不准”,建议按这个顺序排:
- 看字段 mapping
- 看字段是
text还是keyword - 用
_analyze看分词结果 - 检查查询语句是
match、term、还是match_phrase - 看返回结果的
_score - 必要时看 explain/profile
这个顺序比一上来就乱改 DSL 更有效。
13. 练习建议
练习 1:手工建立倒排表
找 3 段短文本,自己手工拆出:
- 词项
- 对应文档编号
把“正排”和“倒排”都写一遍。
练习 2:试 text 和 keyword
创建两个字段:
- 一个
text - 一个
keyword
然后分别用:
matchterm- 聚合
- 排序
观察结果差异。
练习 3:故意制造“搜不到”
把一个字段建成 keyword,然后拿自然语言去 match 它;再把一个字段建成 text,拿 term 去查它。你会更快理解 mapping 和查询类型之间的关系。
14. 自测问题
- 倒排索引和正排索引的区别是什么?
- 为什么 ES 更适合全文搜索?
text和keyword的使用场景有什么不同?- 为什么 analyzer 会直接影响搜索结果?
- 为什么排查 ES 查询问题时,经常要先看
_analyze?
15. 这一章你至少要带走什么
如果你看完只记住 4 件事,就先记这 4 件:
- 倒排索引本质上是“词 -> 文档列表”
- ES 的 Index 不是 MySQL 的索引,它更像文档集合
- Analyzer 决定文本怎么被拆词,也就决定了怎么搜
- 字段类型和查询类型必须匹配,否则很容易搜不准甚至搜不到
把这套心智模型建立起来,后面再学 mapping、DSL、聚合、写入流程,你会轻松很多。