Elasticsearch(以下简称ES)是基于Lucene构建的分布式全文搜索引擎,核心优势在于倒排索引与分布式架构,适用于全文检索、日志分析、实时数据分析等场景。面试中需重点掌握倒排索引原理、写入/搜索流程、集群管理及性能调优四大模块。
一、核心概念与数据模型
- 索引(Index):逻辑上类似关系型数据库的“数据库”,物理上由多个分片组成,支持按时间/业务滚动创建(如
user_logs_2026)。 - 文档(Document):最小数据单元,以JSON格式存储,包含多个字段(Field),每个文档有唯一ID(自动生成或手动指定)。
- 分片(Shard):索引的物理拆分单元,分为主分片(Primary Shard,创建时固定数量,默认5)和副本分片(Replica Shard,动态调整,默认1)。分片分布在集群节点上,主分片处理写入,副本提升读取性能与容错性。
- 节点(Node):ES实例,按角色分为主节点(管理集群元数据)、数据节点(存储数据)、协调节点(分发请求)。
二、倒排索引:ES高效检索的核心
倒排索引是“词项→文档”的反向映射,由**词典(Term Dictionary)和倒排列表(Posting List)**组成,替代传统“文档→词项”的正排索引,实现O(1)级别的关键词检索。
1. 词典(Term Dictionary)
- 存储结构:采用FST(有限状态转换器)压缩存储排序后的词项,共享前缀/后缀减少内存占用(如“倒排索引”“倒排原理”共享“倒排”前缀)。
- 功能:快速定位词项对应的倒排列表,支持前缀匹配、范围查询(如“倒排*”)。
2. 倒排列表(Posting List)
- 内容:包含文档ID、词频(TF,词项在文档中出现次数)、位置(Position,词项在文档中的位置,用于短语查询)、偏移量(Offset,用于结果高亮)。
- 压缩优化:通过差值压缩(FOR算法)存储文档ID差值,VInt编码压缩位置信息,大幅减少存储空间。
三、分词器:倒排索引的“前置处理器”
分词器将文本转换为词项,是影响检索召回率与准确率的关键。核心组件包括:
- Tokenizer:拆分文本为原始词项(如“Elasticsearch倒排索引”拆分为“Elasticsearch”“倒排索引”)。
- TokenFilter:标准化处理(过滤停用词、转小写、词干提取)。
- CharFilter:预处理字符(如替换特殊符号)。
注意:索引时分词器与搜索时分词器必须一致,否则会导致检索不到结果(如索引用ik_max_word,搜索用standard)。
四、写入流程:近实时与数据安全
ES写入遵循“主分片先行,副本同步确认”原则,全链路分为7步:
- 路由计算:协调节点通过
shard = hash(_routing) % primary_shard_count确定目标分片(_routing默认是文档ID)。 - 主分片写入:数据先写入内存缓冲区(Memory Buffer),同时记录到事务日志(Translog)确保数据不丢失。
- 副本同步:主分片将数据同步到所有副本分片,副本确认后返回成功。
- 近实时可见:默认每秒执行一次Refresh,将内存缓冲区数据写入文件系统缓存(Filesystem Cache),生成新的Segment(不可修改),此时数据可被检索。
- 持久化:当Translog达到512MB或30分钟后触发Flush,将Segment写入磁盘,清空Translog。
优化技巧:批量写入时可关闭副本(index.number_of_replicas: 0)和自动Refresh(refresh_interval: -1),写入后恢复,提升性能。
五、搜索流程:Query Then Fetch
搜索分为两个阶段,确保结果准确且高效:
- Query阶段:
- 协调节点广播查询请求到所有相关分片(主或副本)。
- 每个分片在本地执行查询,构建
from + size大小的优先队列(含文档ID和相关性得分)。 - 分片返回结果给协调节点,协调节点合并排序,生成全局结果列表。
- Fetch阶段:
- 协调节点根据文档ID向对应分片请求完整文档数据。
- 分片加载文档并返回,协调节点汇总后返回客户端。
注意:默认搜索类型为query_then_fetch,若需更准确的相关性评分,可使用dfs_query_then_fetch(预查询所有分片的词频和文档频率),但性能较低。
六、集群管理与高可用
1. Master选举
- 条件:仅
node.master: true的节点可参选,通过ZenDiscovery模块(7.x后基于Raft)实现。 - 流程:候选节点按nodeId字典序排序,每个节点投票给排序第一的节点,得票≥
(n/2)+1(n为候选节点数)且自身投票者当选。 - 防脑裂:设置
discovery.zen.minimum_master_nodes = (候选节点数/2)+1,确保集群必须有超过半数候选节点在线才能选举主节点。
2. 故障转移
节点宕机后,集群自动将该节点上的主分片副本升级为主分片,重新分配副本,无需人工干预。
七、性能调优与实战技巧
1. 索引设计
- 滚动索引:按时间创建索引(如
logs_2026-05),通过Rollover API自动滚动,避免单索引过大。 - 冷热分离:热数据(近7天)存SSD,冷数据(7天前)定期执行
force_merge和shrink操作,压缩存储。 - Mapping优化:仅对需检索的字段启用
index: true,高频精确匹配字段用keyword类型(如订单ID),全文检索用text类型并选择合适分词器(如中文用IK)。
2. 写入调优
- 批量写入:使用Bulk API,每批数据控制在5-15MB,减少网络开销。
- 减少副本:初始化写入时关闭副本,写入完成后恢复。
- 自动生成ID:避免手动指定ID的版本冲突开销。
3. 查询调优
- 避免深度分页:
from + size默认限制10000条,深度分页改用Scroll API或Search After。 - 禁用Wildcard:
*keyword会导致全表扫描,改用前缀查询(keyword*)。 - 利用Filter缓存:精确匹配、范围查询用Filter(如
term、range),结果可缓存,性能高于Query。
4. 系统优化
- 内存设置:堆内存≤32GB且≤物理内存50%(留内存给Lucene)。
- 关闭Swap:防止内存交换导致性能雪崩。
- 文件描述符:设置
ulimit -n 65536,支持大量文件句柄。
八、面试高频问题
- 倒排索引与B+树的区别:倒排索引适合全文检索(词项→文档),B+树适合精确匹配(键→值)。
- 为什么ES是近实时:数据写入后需等待Refresh(默认1秒)才能被检索,而非实时可见。
- 如何解决脑裂:候选节点≥3时设置
minimum_master_nodes = (n/2)+1,候选节点=2时改为1主1数据节点。 - text与keyword的区别:text会分词,适合全文检索;keyword不分词,适合精确匹配和聚合。
掌握以上知识点,不仅能应对面试,更能在实际场景中高效使用ES。思考一下:如果让你设计一个电商商品搜索系统,如何结合ES的倒排索引和向量搜索(如ES 8.x的ANN)提升用户体验?