欢迎来到啾啾的博客🐱。
记录学习点滴。分享工作思考和实用技巧,偶尔也分享一些杂谈💬。
有很多很多不足的地方,欢迎评论交流,感谢您的阅读和评论😄。
@TOC
引言
让我们从第一性原理出发,深入了解Elasticsearch(以下简称ES),它在搜索、分析和AI领域扮演重要角色。
让我们一起重构、真正理解它。
资料:
官网
《Elasticsearch: The Definitive Guide》
1 大数据量在的搜索和存储问题
让我们从问题出发: 1、我们要“如何从一百万本 电子书中快速找到提及'引力'这个词的书?”这个方法最关键的部分是什么?
- 最关键的部分是预计算和高效的索引结构,即使用倒排索引(Inverted Index)来实现快速查找,而不是实时扫描所有文档。
2、当数据量大到一台服务器存不下、算不动时,最直接的解决方案是什么?我们该如何分配数据和计算任务?
- 最直接的方案是分布式架构,通过分片(Sharding)来水平扩展存储和计算任务,实现“分而治之”。
在实际生产中,会有海量的非结构化的数据(比如文章、日志、商品描述)需要我们处理。 而像MySQL那样传统的行式数据库逐行扫描的方式太慢,无法胜任快速处理任务。 所以,首先,我们需要不一样的数据结构、需要不一样的数据处理方法。 比如,让方式从“文档 -> 单词”变为“单词 -> 文档”,使用这种倒排索引的方式。
而单服务器无法存储、计算数据的常见方案则是分布式架构,分而治之处理数据。
Elasticsearch——ES就是这样一个成熟的处理方案。
2 ES基础
Elasticsearch 是一个基于 Apache Lucene 构建的分布式搜索和分析引擎、可扩展数据存储以及向量数据库。
文档和分片是ES的基本单元。
2.1 数据结构与数据检索方式
ES数据结构的核心是倒排索引,传统数据库是“文档 -> 单词”,而倒排索引是“单词 -> 文档”。它记录了每个单词出现在哪些文档中,以及具体位置。
数据在ES中以文档形式存在,将一个 JSON 文档(比如 {"title": "探索宇宙", "content": "引力是关键"})交给 Elasticsearch。 Elasticsearch 会对文档内容进行分词 (Tokenization),即把一句话拆成独立的词语(如 "探索", "宇宙", "引力", "是", "关键")。 然后,它更新核心的倒排索引。索引中,“引力”这个词条会指向这个新文档的 ID。这样,当用户搜索“引力”时,Elasticsearch 无需查看每个文档的内容,只需直接在倒排索引中找到“引力”这个词条,就能立即获得所有相关文档的列表。这就是其速度的根源。
ES还使用Doc Values(列式存储)来加速排序和聚合操作,与倒排索引互补。
2.1.1 倒排索引
上文提到过倒排索引是“单词 -> 文档”这样的方式。 它的核心思想是:不要在“搜索时”才去查找,而是在“准备时”就预先处理好一切。
下面,让我们来一起了解一下倒排索引是怎么工作的。
三步法:分词、反转、丰富信息。
step 1 分词
倒排索引的核心在于分词,主流分词有两种思路:基于词典、基于统计。一般是两种结合。现在也可以使用AI进行分词(ES支持集成ML模型如ELSER)。
ES默认使用Standard Analyzer(适合英文),对于中文需安装IK或Jieba插件。分词器不仅会切分词语,通常还会移除没有实际意义的停用词 (Stop Words),比如 的、也、是、,、。。
这里给一个示例,假设我们有下列文档:
- 文档 1 (Doc 1): 首都是北京,北京也是中国的政治中心。
- 文档 2 (Doc 2): 上海是中国的经济中心。
- Doc 1 分词结果 (使用IK插件): 首都, 北京, 北京, 中国, 政治, 中心
- Doc 2 分词结果: 上海, 中国, 经济, 中心
step 2+3 反转+丰富信息
这也是关键的一步,需要创建一个倒排列表,列出所有出现过的、不重复的词语(我们称之为 词条 Term),然后记录下每个词条出现在了哪些文档中。
仅仅知道一个词在哪个文档里出现还不够。为了实现更强大的搜索功能(比如相关性排序、短语搜索),我们需要在倒排列表中记录更丰富的信息。
一个生产级的倒排索引看起来更像这样:
| 词条 (Term) | 文档频率 | 倒排列表 (Postings List) |
|---|---|---|
| 上海 (shàng hǎi) | 1 | [ (Doc 2, 1, [1]) ] |
| 中心 (zhōng xīn) | 2 | [ (Doc 1, 1, [6]), (Doc 2, 1, [4]) ] |
| 中国 (zhōng guó) | 2 | [ (Doc 1, 1, [4]), (Doc 2, 1, [2]) ] |
| 首都 (shǒu dū) | 1 | [ (Doc 1, 1, [1]) ] |
| 政治 (zhèng zhì) | 1 | [ (Doc 1, 1, [5]) ] |
| 经济 (jīng jì) | 1 | [ (Doc 2, 1, [3]) ] |
| 北京 (běi jīng) | 1 | [ (Doc 1, 2, [2, 3]) ] |
我们来详细解读 北京 这一行:
- 词条:北京。
- 文档频率:1。表示 北京 这个词总共只在 1 个文档里出现过(虽然在那个文档里出现了多次)。
- 倒排列表:
[ (Doc 1, 2, [2, 3]) ]- Doc 1: 出现在文档 1。
- 2: 词频。表示 北京 在文档 1 中出现了 2 次。
[2, 3]: 位置。表示它在文档 1 中是第 2 个和第 3 个词条。
有了这样的倒排索引,搜索时,系统不需要再扫描任何文档,而是直接在“列表”中通过高效的查找算法(类似二分查找)定位到 search 这个词条。 然后获取词条的倒排列表完成搜索。
还有布尔查询,搜索 a and b,则分别搜索,对获取到的倒排列表做交集等操作(AND/OR/NOT)。ES的Query DSL支持更复杂的MUST/SHOULD/FILTER。
示例查询(搜索“北京 AND 中心”):
{
"query": {
"bool": {
"must": [
{ "match": { "content": "北京" } },
{ "match": { "content": "中心" } }
]
}
}
}
2.2 分布式方案
Elasticsearch 将整个索引库(所有文档和它们的倒排索引)水平切分成多个小块,每一块就是一个分片 (Shard)。
并且为了解决分布式网络问题,防止分片服务器宕机导致数据丢失,Elasticsearch 为每个分片创建了一个或多个完全相同的复制品,称为副本 (Replica)。 这些副本会被存放在不同的服务器上。 当主分片宕机时,一个副本会立刻被提升为新的主分片,确保数据的安全和服务的高可用性。副本还能分担搜索请求,提升查询性能(通过Preference参数实现读负载均衡)。
每个分片本身就是一个功能齐全、独立的搜索引擎(基于Lucene)。当搜索时,Elasticsearch 会将请求发送给所有相关的分片,然后将各个分片返回的结果合并成最终的完整列表。这解决了单机存储和计算的瓶颈。
注意:主分片数在创建索引时固定,无法后期更改(需Reindex),这是常见设计坑。
2.2.1 ES的路由
当搜索时(读操作):请求会被广播到所有相关分片(通常是所有分片,但可通过Routing优化避免全广播),因为数据可能存在于任何一个分片中。这是一种“一对多”的通信模式。 存储时(写操作):请求会通过路由公式精确定位到唯一一个主分片。这是一种“一对一”的通信模式。数据写入这个主分片后,再由这个主分片同步到它的各个副本分片上。
存储
在存储时,ES设置怎样的机制来找相关的分片,怎样路由呢?
一个简单的公式:shard_num = hash(_routing) % number_of_primary_shards,公式解读如下:
-
_routing:这是一个路由值。默认情况下,它就是文档的_id。你也可以在存入文档时手动指定它,但我们先考虑默认情况。 -
hash(...):这是一个哈希函数,它能将一个字符串(比如文档的_id)转换成一个数字。关键在于,对于同一个字符串,它每次计算出的数字都是完全相同的。 -
% number_of_primary_shards:这是取模运算。用哈希计算出的数字,除以主分片的总数,然后取余数。结果必然是一个介于 0 和 (主分片数 - 1) 之间的整数,这个数字就是目标分片的编号。
查询
而执行搜索查询时,相关的分片通常是“所有分片”,查询请求会被广播到索引中的每一个分片,每个分片都在自己的“小搜索引擎”内部执行查询,找到所有匹配的文档,然后将结果(通常是文档 ID 和相关性得分)返回给协调节点。 协调节点收集所有分片的结果,进行汇总、排序,最后将最终的、完整的列表返回。 对于特定查询(如Get by ID),可路由到单一分片。
2.3 聚合
ES 不仅能“搜”,还能“算”。它可以对搜索出的数据进行实时聚合,生成统计图表。类似于 SQL 中的 GROUP BY + 聚合函数(如 COUNT, AVG, SUM),但功能更强大,支持多层嵌套、桶(Bucket)与指标(Metric)组合。
ES执行查询、筛选出匹配的文档后,在聚合阶段,根据聚合定义(如 terms 字段),对文档进行分桶,在每个桶内执行指标聚合(如计算平均响应时间)。支持多层嵌套(例如:先按服务名分桶,再在每个服务桶内按时间分桶)。Doc Values(列式存储)加速了这一过程。
最后协调节点汇总各分片的聚合结果,返回最终结果。
这也是 Kibana 可视化仪表盘背后的核心技术。
2.3.1 聚合类型
ES 聚合主要分为三类:
| 类型 | 说明 | 示例 | 潜在挑战 |
|---|---|---|---|
| Bucket 聚合 | 将文档分组到“桶”中(类似分组) | terms(按字段值分组)、date_histogram(按时间分桶) | 大基数字段可能导致内存爆炸 |
| Metric 聚合 | 对数值字段进行统计计算 | avg, sum, min, max, percentiles | 需要Doc Values启用 |
| Pipeline 聚合 | 对其他聚合的结果再进行计算 | derivative(求导)、moving_avg(移动平均) | 计算开销大,适合时间序列 |
| 示例聚合(按城市分组,计算平均响应时间): |
{
"aggs": {
"by_city": {
"terms": { "field": "city" },
"aggs": {
"avg_response": { "avg": { "field": "response_time" } }
}
}
}
}
2.4 多层缓冲
ES实现了 “近实时”搜索。
磁盘 I/O 是非常昂贵的。如果每次写入都直接强制刷盘(fsync),性能会极差。ES的解决方案是多层缓冲:
-
新写入的文档首先进入内存缓冲区 (In-memory buffer)。
-
默认每隔 1 秒,会执行一次 refresh 操作。这个操作会将内存缓冲区的数据转换成一个新的段 (Segment) 并写入到文件系统缓存 (File system cache) 中。此时,文档就可以被搜索到了,但数据并未持久化到磁盘,断电会丢失。这就是“近实时”的来源。Lucene的段是不可变的,这优化了读性能。
-
当数据积累到一定程度或时间,会执行 flush 操作。这个操作会真正将文件系统缓存中的数据通过 fsync 持久化到磁盘,并清空事务日志 (Translog)。Translog 是WAL(Write-Ahead Log),用于崩溃恢复,确保数据耐久性。
调优参数:如index.refresh_interval=30s,可权衡搜索延迟和写入性能。
3 ES核心应用场景
3.1 需要“可观测性”场景(APM)
当一个复杂的分布式系统(如一个大型网站)出现故障时,如何在每秒产生的数百万条日志和指标数据中,瞬间定位到那条导致问题的“罪魁祸首”?
这种传统方式“观测”困难的场景是ES最庞大、最成熟的应用领域。
日志本质上是带有时间戳的文本。 而ES的倒排索引让我们可以对海量的、非结构化的日志内容进行闪电般的全文搜索(例如,搜索所有包含 "Error: NullPointerException" 的日志)。 ES的分片机制,则允许系统随着数据量的增长而无缝地水平扩展。聚合能力则可以计算错误数量等进行指标统计。
Elasticsearch 使您能够使用 Elastic 的统一平台为网站、应用程序和企业数据构建强大的搜索体验。
3.2 AI-powered search
文档:www.elastic.co/docs/soluti…
以下是一些常见的实际应用(来自官网):
| Use case 用例 | Business goals 业务目标 | Technical requirements 技术要求 |
|---|---|---|
| Vector search/hybrid search 向量搜索/混合搜索 | Run nearest neighbour search, combine with text for hybrid results 运行最近邻搜索,结合文本以获得混合结果 | Dense embeddings, sparse embeddings, combined with text/BM25 稠密嵌入、稀疏嵌入,结合文本/BM25 |
| Ecommerce/product catalog search 电子商务/产品目录搜索 | Provide fast, relevant, and up-to-date results, faceted navigation 提供快速、相关且最新的结果,支持分面导航 | Inventory sync, user behavior tracking, results caching 库存同步,用户行为跟踪,结果缓存 |
| Workplace/knowledge base search 工作场所/知识库搜索 | Search across range of data sources, enforcing permissions 跨多个数据源搜索,强制执行权限 | Third-party connectors, document-level security, role mapping 第三方连接器、文档级安全、角色映射 |
| Website search 网站搜索 | Deliver relevant, up-to-date results 提供相关、最新的结果 | Web crawling, incremental indexing, query caching 网络爬虫、增量索引、查询缓存 |
| Customer support search 客户支持搜索 | Surface relevant solutions, manage access controls, track metrics 展示相关解决方案,管理访问控制,追踪指标 | Knowledge graph, role-based access, analytics 知识图谱、基于角色的访问控制、分析 |
| Chatbots/RAG 聊天机器人/检索增强生成 | Enable natural conversations, provide context, maintain knowledge 支持自然对话,提供上下文,保持知识 | Vector search, ML models, knowledge base integration 向量搜索,机器学习模型,知识库集成 |
| Geospatial search 地理空间搜索 | Process location queries, sort by proximity, filter by area 处理位置查询,按距离排序,按区域筛选 | Geo-mapping, spatial indexing, distance calculations 地理映射,空间索引,距离计算 |
| 实战示例:在RAG中,使用ES存储Embeddings: |
- 安装ELSER模型。
- Index时生成向量:使用Inference Pipeline。
- 查询:kNN或Hybrid Search。
4 面试问题
Q:Elasticsearch 是什么?它和传统数据库(如 MySQL)最根本的区别是什么?
它首先是一个搜索引擎和分析引擎,其次才是一个数据存储。它的核心使命是解决“在海量数据中进行快速、相关的全文检索和聚合分析”这个根本问题。 MySQL 的基石是 B+Tree,为事务性的精确读写(CRUD)而优化。Elasticsearch 的基石是倒排索引,为“搜索”这个特定场景而优化。这个根本区别决定了它们后续所有的设计差异(如事务支持、写入成本、JOIN 的处理方式等)。
| 方面 | MySQL | ES |
|---|---|---|
| 数据结构 | 行式/B+Tree | 倒排索引 + Doc Values |
| 搜索效率 | O(n) 扫描 | O(log n) 查找 + 相关性评分 |
| 扩展性 | 垂直扩展为主 | 水平扩展(分片) |
Q:什么是倒排索引?请解释一下它的工作原理?
传统“正向索引”(文档 -> 单词)在搜索时需要进行全局扫描,效率极低。 倒排索引通过“单词 -> 文档”的映射,将搜索问题从一个高复杂度的“计算问题”转换成了一个低复杂度的“查找问题”。 通过分词、反转、丰富信息,构建倒排列表存储词频和位置等信息。
Q:假如你负责设计一个日志分析系统,你会如何设计 Elasticsearch 的索引和集群?
-
数据特性:日志是典型的时间序列数据,写入频繁,基本不更新,且查询通常集中在近期数据。
-
索引设计:
- 按时间分索引:绝不能用一个大索引存所有日志。应该按天或按月创建新索引(如 logs-2025-09-26)。这便于管理和删除旧数据。
- 使用数据流 (Data Streams) 和 索引生命周期管理 (ILM):这是现代 ES 的标准答案。ILM 可以自动化地管理索引,例如:30 天内的数据存放在高性能的“热”节点上,30-90 天的数据自动迁移到低成本的“温”节点,超过 90 天的数据自动备份到“冷”节点或直接删除。
-
集群设计:采用热温冷架构 (Hot-Warm-Cold Architecture),使用不同配置的节点来存储不同生命周期阶段的数据,实现成本和性能的最佳平衡。避免脑裂问题,通过discovery.seed_hosts配置。
Q:Elasticsearch 集群突然变慢了,要从哪些方面去排查问题?
- 宏观到微观:先看整体,再看局部。
-
监控先行:检查集群的监控仪表盘。关键指标包括:CPU 使用率、JVM 堆内存使用率(是否频繁 GC)、磁盘 I/O、网络流量。使用Hot Threads API诊断。
-
查询层面:
- 是不是有“慢查询”?通过 Slow Log 功能定位到具体的慢查询。
- 分析慢查询的 explain 报告,看是聚合查询开销大,还是查询语句本身有问题(如前缀通配符查询
*text)。使用Search Profiler API剖析。
-
索引层面:
- 索引设计是否合理?分片是否过大或过小?(单个分片建议在 10GB-50GB)。
- 映射 (Mapping) 是否存在问题?比如动态映射导致的字段类型不当,或字段数量过多(映射爆炸,默认限1000字段)。
-
集群与节点层面:
- 是否有节点负载不均?
- 集群是否处于 yellow 或 red 状态?(是否有未分配的分片)。检查
_cat/health。
额外问题:如何处理Deep Pagination?使用From/Size有性能问题,推荐Scroll或Search After。
总结
总的来说,ES特性是大数量下的高质量搜索、数据存储。
存储就需要解决I/O问题,也是类似Kafka的缓冲+批处理。还有索引生命周期(文件管理)。
搜索就需要高效的数据结构,ES设计的就是倒排索引,是空间换时间的方案,做了大量的预处理。
而分布式集群、架构需要解决的问题也是一样需要解决,如分片与分片副本提升可用性、可靠性、可扩展性。 明确的路由与广播协作。