什么是搜索:百度、垂直搜索(站内搜索)
搜索:通过一个关键词或一段描述,得到你想要的(相关度高)结果。
如何实现搜索功能?
关系型数据库:性能差、不可靠、结果不准确(相关度低)。特别是文本类的查询。主要是基于B+tree,B-Tree结构图中可以看到每个节点中不仅包含数据的key值,还有data值。而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度。
B+Tree相对于B-Tree有几点不同:
- 非叶子节点只存储键值信息。
- 所有叶子节点之间都有一个链指针。
- 数据记录都存放在叶子节点中。
将上一节中的B-Tree优化,由于B+Tree的非叶子节点只存储键值信息,假设每个磁盘块能存储4个键值及指针信息,则变成B+Tree后其结构如下图所示

正排索引:由Key到value。
倒排索引:由value到key。
当用户在主页上搜索关键词“华为手机”时,假设只存在正向索引(forward index),那么就需要扫描索引库中的所有文档,找出所有包含关键词“华为手机”的文档,再根据打分模型进行打分,排出名次后呈现给用户。因为互联网上收录在搜索引擎中的文档的数目是个天文数字,这样的索引结构根本无法满足实时返回排名结果的要求。
所以,搜索引擎会将正向索引重新构建为倒排索引,即把文件ID对应到关键词的映射转换为关键词到文件ID的映射,每个关键词都对应着一系列的文件,这些文件中都出现这个关键词。


ElasticSearch的数据结构:
单词ID:记录每个单词的单词编号;
单词:对应的单词;
文档频率:代表文档集合中有多少个文档包含某个单词
倒排列表:包含单词ID及其他必要信息
DocId:单词出现的文档id
TF:单词在某个文档中出现的次数
POS:单词在文档中出现的位置
以单词“加盟”为例,其单词编号为6,文档频率为3,代表整个文档集合中有三个文档包含这个单词,对应的倒排列表为{(2;1;<4>),(3;1;<7>),(5;1;<5>)},含义是在文档2,3,5出现过这个单词,在每个文档的出现过1次,单词“加盟”在第一个文档的POS是4,即文档的第四个单词是“加盟”,其他的类似。
这个倒排索引已经是一个非常完备的索引系统,实际搜索系统的索引结构基本如此。
Lucene
Lucene:jar包,帮我们创建倒排索引,提供了复杂的API。
如果使用Lucene做集群实现搜索,会有以下问题:
- 节点一旦宕机,数据丢失,后果不堪设想,可用性差。
- 自己维护,麻烦(自己创建管理索引),单台节点的承载请求的能力是有限的,需要人工做负载(雨露均沾)。
ElasticSearch:分布式、高性能、高可用、高扩展
分布式的搜索,存储和数据分析引擎:
ElasticSearch≠搜索引擎,他只是作为全文检索来使用。
优点
- 面向开发者友好,屏蔽了Lucene的复杂特性,集群自动发现(cluster discovery)
- 自动维护数据在多个节点上的建立。
- 会自动做搜索请求的负载均衡。
- 自动维护冗余副本,保证了部分节点宕机的情况下仍然不会有任何数据丢失。
- ElasticSearch基于Lucene提供了很多高级功能:复合查询、聚合分析、基于地理位置。
- 对于大公司,可以构建几百台服务器的大型分布式集群,处理PB级别数据;对于小公司,开箱即用,门槛低上手简单。
- 相比传统数据库,提供了全文检索,同义词处理(美丽的cls>漂亮的cls),相关度排名。聚合分析以及海量数据的近实时(NTR)处理,这些传统数据库完全做不到。
应用领域:
- 百度(全文检索、高亮、搜索推荐)
- 各大网站的用户行为日志(用户点击、浏览、收藏、评论)
- BI(Business Intelligence商业智能),数据分析,数据挖掘统计。
- Github:代码托管平台,几千亿行代码。
- ELK:Elasticsearch(数据检索),Logstash(日志采集),Kibana(可视化)。
Elasticsearch核心概念
- cluster(集群):每个集群至少包含两个节点。
- Node:集群的每个节点,一个节点不代表一台服务器。
- Field:一个数据字段,与index和type一起,可以定位一个doc。
- Document:elasticsearch最小的数据单元,JSON格式。
- Type:逻辑上的数据分类。
底层算法
Frame Of Reference(FOR):压缩数据,减少磁盘占用空间,所以当我们从磁盘取数据时,也需要一个反向的过程,即解压。
Roaring Bitmaps(RBM):快速求交并集。
BM25和TF-IDF算法。提高了准确率和召回率。
倒排索引底层数据结构

Lucene 的倒排索,增加了最左边的一层「字典树」term index,它不存储所有的单词,只存储单词前缀,通过字典树找到单词所在的块,也就是单词的大概位置,再在块里二分查找,找到对应的单词,再找到单词对应的文档列表。
当然,内存寸土寸金,能省则省,所以 Lucene 还用了 FST(Finite State Transducers)对它进一步压缩。
FST 是什么?这里就不展开了,这次重点想聊的,是最右边的 Posting List 的,别看它只是存一个文档 ID 数组,但是它在设计时,遇到的问题可不少。如何压缩以节省磁盘空间。如何快速求交并集。Frame Of Reference(FOR)解决了这两个问题。
基础概念
Elasticsearch 是什么?与关系型数据库有什么区别?
答案:
- 定义:Elasticsearch(ES)是一个分布式的、基于 Lucene 的开源搜索和分析引擎,常用于全文检索、日志分析、实时监控等。
- 区别:
- 存储结构:ES 存 JSON 文档,关系型数据库存行列数据。
- 索引方式:ES 使用倒排索引,数据库用 B+ 树索引。
- 查询能力:ES 擅长模糊搜索、全文检索,数据库擅长事务与结构化查询。
- 扩展性:ES 天然分布式,RDBMS 扩展困难。
倒排索引是什么?为什么 Elasticsearch 使用倒排索引?
答案:
- 倒排索引:存储“单词 -> 文档列表”的映射。比如 “搜索” 出现于 [doc1, doc5, doc7]。
- 优点:可以快速定位包含某个关键词的文档,支持高效的全文检索和分词搜索。
- 为什么用它:相比 B+ 树(适合范围查询),倒排索引更适合搜索引擎场景。
Elasticsearch 中的文档、索引、分片、节点的关系?
答案:
- 文档(Document):基本数据单元(JSON 格式)。
- 索引(Index):文档集合(类似数据库的“库”)。
- 分片(Shard):索引的物理存储单位,一个索引被拆分成多个分片分布在不同节点。
- 副本(Replica):分片的复制,用于高可用和读负载均衡。
- 节点(Node):ES 集群中的一个实例,可以持有多个分片。
核心原理
Elasticsearch 的写入流程是怎样的?
答案:
- 客户端发送 写请求。
- 协调节点(Coordinator Node)将请求路由到 主分片(Primary Shard) 所在的节点。
- 主分片执行写操作,写入内存 buffer 和 translog。
- 写操作同步到 副本分片(Replica Shard)。
- 主分片和副本都成功后,返回客户端成功响应。
Elasticsearch 的搜索流程是怎样的?
答案:
分两阶段(Query Then Fetch):
- Query 阶段:
- 协调节点将查询请求发送到相关分片。
- 每个分片独立执行查询,返回匹配文档的 topN 结果(docID + score)。
- Fetch 阶段:
- 协调节点收集所有分片结果,重新排序,选出最终 topN。
- 根据 docID 从对应分片拉取实际文档,返回给客户端。
Elasticsearch 是如何实现高可用的?
答案:
- 副本机制:每个分片有多个副本,主分片挂掉后,副本会提升为主分片。
- 分布式选举:通过 Zen Discovery 或 Cluster Coordination 算法选举 master 节点。
- 路由机制:协调节点负责请求转发,避免单点问题。
Elasticsearch 的分词器(Analyzer)原理?
答案:
分词器由 三部分组成:
- 字符过滤器(Character Filter):处理输入文本(如去掉 HTML 标签)。
- 分词器(Tokenizer):按规则切分词语(如空格、中文分词)。
- 词元过滤器(Token Filter):对分词结果进一步处理(小写化、同义词扩展、停用词过滤)。
常见分词器:
standard:默认分词器,按 Unicode 规则。ik_max_word/ik_smart(中文常用)。whitespace:按空格分词。
性能与优化
如何优化 Elasticsearch 查询性能?
答案:
- 索引层面:
- 合理设置分片数量(不要过多,避免小分片问题)。
- 使用合适的分词器,避免过度分词。
- 使用 keyword 类型存储精确匹配字段。
- 查询层面:
- 使用
filter代替query(filter 不计算相关性,可缓存)。 - 避免
wildcard和regexp,改用前缀索引。 - 控制返回字段(
_source只取需要的字段)。
- 使用
- 硬件层面:
- 增加节点,提升并行查询能力。
- 使用 SSD 提升 IO 性能。
Elasticsearch 为什么查询速度快?
答案:
- 倒排索引,快速定位文档。
- Segment 文件不可变,支持并发访问。
- 基于内存的缓存机制(filter cache, fielddata, query cache)。
- 分布式并行查询,多节点协同。
10. 如何设计 Elasticsearch 的分片数?
答案:
- 分片太少:扩展性差,单分片过大,影响迁移和恢复。
- 分片太多:管理开销大,内存占用高。
- 经验:
- 每个分片大小控制在 10GB~50GB。
- 根据数据量和节点数计算:
分片数 ≈ 数据量 / 30GB / 节点数。 - 可以用
shrink/splitAPI 动态调整。
集群管理
Elasticsearch 中的 Master 节点职责?
答案:
- 负责集群状态管理(分片分配、节点加入/退出)。
- 不处理数据写入/查询(除非也是 data 节点)。
- 防止脑裂(split brain),需要设置
minimum_master_nodes(7.x 之前)。
脑裂问题是什么?如何避免?
答案:
- 脑裂:集群中出现多个 master 节点,导致分片分配混乱。
- 原因:
- 网络分区。
minimum_master_nodes设置不合理。
- 解决:
- 设置
discovery.zen.minimum_master_nodes = (N/2+1)。 - 使用单独的专用 master 节点。
- 设置
Elasticsearch 的刷新(refresh)、合并(merge)、刷盘(flush)区别?
答案:
- refresh:将内存 buffer 写入 segment,生成新搜索结果(默认 1s 刷新一次)。
- merge:小 segment 合并成大 segment,减少文件数量。
- flush:清空 translog,将数据写入磁盘,保证持久化。
实践问题
如何实现 Elasticsearch 的数据迁移?
答案:
- 使用 reindex API(跨索引迁移)。
- 使用 snapshot & restore(备份和恢复)。
- 使用 logstash / elasticdump 导入导出数据。
如果 Elasticsearch 集群写入压力大,怎么优化?
答案:
- 批量写入(
bulk API)。 - 降低副本数,写入完成后再增加。
- 使用异步写入。
- 合理设计 mapping,避免动态 mapping 频繁更新。
- 调整刷新间隔:
index.refresh_interval = -1(大批量导入时)。
Elasticsearch 中 mapping 的动态映射(dynamic mapping)是什么?
答案:
- ES 自动识别新字段并映射类型。
- 例如:
"age": 18自动识别为 integer。 - 风险:可能产生错误类型(如字符串识别成 text 而非 keyword),导致搜索或聚合异常。
- 解决:推荐手动定义 mapping,关闭动态映射或限制。
高级问题
Elasticsearch 和 Solr 的区别?
答案:
- 架构:ES 天然分布式,Solr 依赖 ZooKeeper。
- 实时性:ES 更强(默认 1s refresh),Solr 偏批处理。
- 易用性:ES 提供 RESTful API,Solr XML 配置较复杂。
- 社区生态:ES 在日志(ELK/EFK)领域更主流。
Elasticsearch 聚合(Aggregation)的原理?
答案:
- 聚合在分片本地先执行,产生局部结果。
- 协调节点收集所有分片的结果,进行全局归并。
- 类似 MapReduce:分片本地计算(Map)+ 协调节点汇总(Reduce)。
Kibana 与 Elasticsearch 的关系?
答案:
- Elasticsearch:负责存储、检索数据。
- Kibana:可视化工具,通过 REST API 查询 ES。
- 常用于日志分析、监控展示。
Elasticsearch 的常见使用场景?
答案:
- 全文搜索(电商搜索、文章检索)。
- 日志系统(EFK/ELK Stack)。
- 实时监控和告警(APM)。
- 数据分析(聚合 + 可视化)。
