面试题
一、基础概念类
1. 什么是 Elasticsearch?它的主要用途是什么?
答:
Elasticsearch 是一个基于 Lucene 的开源分布式搜索和分析引擎,支持实时全文检索、结构化搜索、日志分析、监控数据等场景。
主要用途:
- 全文搜索引擎(如电商商品搜索)
- 日志和指标分析(结合 Logstash、Kibana 构成 ELK 栈)
- 实时数据分析平台
- 数据聚合与可视化
2. Elasticsearch 是如何实现近实时搜索的?
答:
Elasticsearch 默认每 1 秒刷新一次(refresh interval),将内存中的文档写入倒排索引,使其可被搜索,因此被称为“近实时”(NRT, Near Real-Time)。
- 新文档先写入内存 buffer 和 translog(事务日志)
- 每隔 1 秒执行一次
refresh,生成新的 segment 并打开供搜索 - 可通过
?refresh参数手动触发刷新 - 注意:
refresh不等于持久化,数据仍需通过flush写入磁盘
3. Elasticsearch 中的倒排索引来是什么?它有什么优势?
答:
倒排索引(Inverted Index)是 Elasticsearch 实现快速全文搜索的核心数据结构。
结构示例:
1 | 词项 → 包含该词的文档列表 |
优势:
- 快速定位包含某个关键词的文档
- 支持布尔查询、短语匹配、高亮等功能
- 相比正向索引(文档 → 词项),更适合全文检索
4. Elasticsearch 是分布式的,它的分布式架构是怎样的?
答:
Elasticsearch 的分布式架构包括以下几个核心概念:
| 组件 | 说明 |
|---|---|
| Node(节点) | 一个运行中的 ES 实例 |
| Cluster(集群) | 由一个或多个节点组成,共同管理数据 |
| Index(索引) | 类似数据库中的“表”,存储一类文档 |
| Shard(分片) | 索引被分为多个 shard,实现水平扩展 |
| Replica(副本) | 每个 shard 可有多个副本,提高容错性和吞吐量 |
- 主分片(Primary Shard):负责写操作
- 副本分片(Replica Shard):用于读操作和故障恢复
- 分布式查询通过协调节点(Coordinating Node)完成
二、核心机制
5. Elasticsearch 写入数据的流程是怎样的?
答:
写入流程(Index API)如下:
- 客户端发送写请求到任意节点(协调节点)
- 协调节点根据
_id计算目标分片:shard = hash(_id) % primary_shard_count - 请求转发到主分片所在节点
- 主分片将数据写入内存 buffer 并追加到 translog
- 执行 refresh(默认 1s 一次),使文档可被搜索
- 定期执行 flush,将内存 buffer 中的数据写入磁盘 segment,并清空 translog
- 主分片通知副本分片同步数据,完成后返回客户端成功
✅ 成功条件:主分片 + 所有副本分片都成功写入(取决于
wait_for_active_shards设置)
6. 什么是 translog?它的作用是什么?
答:
translog(Transaction Log)是一个事务日志,记录了所有对索引的写操作(index、update、delete)。
作用:
- 保证数据不丢失:即使数据还在内存中未刷新到 segment,崩溃后可通过 translog 恢复
- 支持实时 get 操作:可通过 translog 获取最新版本的文档
- flush 操作会清空 translog(或生成新的)
默认每 5 秒或每次
index.refresh_interval后 fsync 到磁盘(可配置)
7. 说说 refresh、flush、merge 的区别?
| 操作 | 说明 | 触发频率 |
|---|---|---|
| refresh | 将内存 buffer 中的数据生成新的 segment,使其可被搜索 | 默认 1s 一次 |
| flush | 将内存中的 segment 写入磁盘,并清空 translog | 默认 30 分钟或 translog 太大时 |
| merge | 合并多个小 segment 为大 segment,减少文件数,提升性能 | 后台自动进行 |
merge 是 I/O 密集型操作,但不阻塞读写
8. Elasticsearch 如何保证高可用?
答:
通过以下机制实现高可用:
- 副本机制(Replica):每个主分片可有多个副本,当主分片宕机时,副本可提升为主分片
- 集群自动发现与选举:使用 Zen Discovery 或基于 Raft 的新协调机制(7.x+)选举 master 节点
- 数据分片与负载均衡:数据分散在多个节点上,避免单点故障
- 脑裂防护:通过设置
discovery.zen.minimum_master_nodes(旧版本)或cluster.initial_master_nodes(新版本)防止脑裂
三、查询与性能优化
9. 介绍一下常用的查询类型?
答:
常见查询类型包括:
| 查询类型 | 示例 | 用途 |
|---|---|---|
match | {"match": {"title": "elastic"}} | 全文匹配,会分词 |
term | {"term": {"status": "active"}} | 精确匹配,不分词 |
match_phrase | "quick brown fox" | 短语匹配,顺序一致 |
bool | must,should,must_not | 组合多个条件 |
range | {"age": {"gte": 18}} | 范围查询 |
wildcard | {"name": {"wildcard": "abc*"}} | 通配符查询(性能较差) |
prefix | 自动前缀匹配 | 适合补全场景 |
⚠️
term查询用于 keyword 类型;match用于 text 类型
10. 什么是相关性评分(_score)?它是如何计算的?
答:_score 表示文档与查询的相关性得分,基于 TF-IDF 或 BM25 算法(ES 5.0+ 默认使用 BM25)。
BM25 公式简化理解:
- 词频(TF)越高,得分越高
- 逆文档频率(IDF)越高(词越稀有),得分越高
- 字段长度越短,得分越高
可通过
explain=true查看评分细节
11. 如何优化 Elasticsearch 的搜索性能?
答:
常见优化手段:
- 合理设计 Mapping:
- 避免 dynamic mapping 导致字段类型错误
- 使用
keyword替代text用于聚合和精确查询
- 调整分片数量:
- 单个分片建议 10–50GB,避免过多分片
- 使用 filter 上下文:
filter不计算评分,可缓存,性能更高
- 避免 deep paging:
- 使用
search_after或scroll替代from + size
- 使用
- 冷热架构分离:
- 热节点处理写入和实时查询,冷节点存储历史数据
- 开启慢查询日志:
- 分析并优化慢查询
- 使用预聚合或物化视图(8.x 支持)
四、运维与实战类
12. 如何设计一个适合电商商品搜索的索引结构?
答:
示例 mapping:
1 | PUT /product_index |
搜索示例:
1 | GET /product_index/_search |
使用 IK 分词器支持中文分词,
filter提升性能
13. Elasticsearch 出现“Too Many Requests”或“Bulk Rejection”怎么办?
答:
这是线程池拒绝请求的常见问题,原因和解决方案如下:
原因:
- 写入或查询请求过多,超出线程池处理能力
- 系统资源不足(CPU、内存、磁盘 IO)
解决方案:
增加线程池队列大小(谨慎):
1
2
3thread_pool:
write:
queue_size: 1000降低 bulk 写入速率,增加
--bulk-size间隔增加节点或分片,分散负载
升级硬件配置
使用
retry_on_conflict和指数退避重试机制
14. 如何安全地升级 Elasticsearch 集群?
答:
推荐滚动升级(Rolling Upgrade)流程:
查看官方兼容性文档(如 7.x → 8.x 可能需停机)
备份数据(快照)
关闭 shard 分配:
1
2PUT /_cluster/settings
{ "transient": { "cluster.routing.allocation.enable": "primaries" } }逐个停止节点,升级软件,重启
重新启用分配:
1
2PUT /_cluster/settings
{ "transient": { "cluster.routing.allocation.enable": "all" } }验证集群健康状态:
GET /_cluster/health
⚠️ 跨大版本升级可能需要重新索引
15. Elasticsearch 中的脑裂问题是什么?如何避免?
答:
脑裂(Split Brain):网络分区导致多个 master 节点同时存在,造成数据不一致。
避免方法:
设置正确的 master 节点选举最小数量:
1
2
3
4
5# 旧版本(7.x 之前)
discovery.zen.minimum_master_nodes: 3 # 对于 5 节点集群
# 新版本(7.x+)使用 quorum-based coordination
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]建议奇数个 master-eligible 节点(3、5、7)
配置合理的
discovery.seed_hosts和cluster.name
五、附加题(高级)
16. 说说 Elasticsearch 中的聚合(Aggregation)类型?
答:
三大类聚合:
Metric Aggregation
(指标聚合):
avg,sum,min,max,cardinality(去重统计)
Bucket Aggregation
(桶聚合):
terms:按字段值分桶range:按范围分桶date_histogram:按时间间隔分桶
Pipeline Aggregation
(管道聚合):
- 对其他聚合结果进行二次计算,如
derivative,moving_avg
- 对其他聚合结果进行二次计算,如
示例:统计各品类销售额
1 | "aggs": { |
17. Elasticsearch 支持哪些分词器?如何自定义?
答:
常见分词器:
| 分词器 | 说明 |
|---|---|
standard | 默认,按词边界分词 |
whitespace | 按空格分 |
keyword | 不分词,整个字段作为一个 term |
simple | 按非字母字符分 |
language | 支持中文、英文等语言(如english) |
ik(第三方) | 中文分词,支持ik_smart和ik_max_word |
自定义 analyzer 示例:
1 | PUT /my_index |
需提前安装 ik 分词插件:
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/...
18. Elasticsearch 和数据库(如 MySQL)有什么区别?
| 对比项 | ELASTICSEARCH | MYSQL |
|---|---|---|
| 存储模型 | 倒排索引 + 正排索引 | B+ 树索引 |
| 查询类型 | 全文搜索、模糊匹配 | 精确查询、JOIN |
| 一致性 | 最终一致性 | 强一致性 |
| 写入性能 | 高(批量写入) | 相对较低 |
| 适用场景 | 搜索、日志分析 | 事务处理、关系数据 |
建议:ES 不替代数据库,而是作为查询加速层
实操
1、安装ElasticSearch
Ubuntu18.04中安装参考 [^1]
ubuntu使用命令行apt安装,mac下载解压使用,下面示例主要使用的是mac系统下面的es版本7.12.0
1 | 安装jdk |
使用 curl -X GET “localhost:9200/“ 测试是否启动成功
1 | { |
若在服务器中启动失败,可能为内存太小,修改配置文件**/etc/elasticsearch/jvm.options **
1 | # -Xms4g |
配置远程连接,则设置elasticsearch.yml 文件
1 | 设置为0.0.0.0 |
2、ES可视化elasticsearch-head
elasticsearch-head Gtihub地址 [^2]
1 | 进入到项目目录 |
连接时遇到跨域问题

解决:修改elasticsearch.yml配置文件,增加下面跨域配置后重新启动
1 | http.cors.enabled: true |
3、使用Kibana(版本需与ES一致)
Kibana下载地址 [^3]
解压后需要先启动es再启动kibana,默认端口为5601
设置中文,修改kibana.yml配置文件
1 | i18n.locale: "zh-CN" |
4、使用ik分词器(版本需与ES一致)
iK分词器下载地址 [^4]
ik提供的两个算法:
- ik_smart:最少切分
- ik_max_word:最细粒度划分
下载解压后放入到es的plugins中,并重命名文件夹名称

测试查看已有的插件

重新启动es与kibana,从kibana中测试ik分词器,字典中查找
ik_smart:最少切分

ik_max_word:最细粒度划分

未自定义之前

在ik配置文件中添加自己自定义的dic文件

如果需要自定义的分词,多个自定义的dic文件使用 “**;”** 间隔

修改完后重新启动

变成自定义的单词

5、索引文档操作
创建索引PUT
不推荐使用类型名,换成 “_doc” 使用
1 | PUT /索引名/类型名(后期可不写)/id |

创建完成后

ES的类型可以有多个
- 字符串类型:text、keyword
- 数值类型:long、integer、short、byte、double、float、half float、scaled float
- 日期类型:date
- 布尔类型:boolean
- 二进制类型:binary
指定字段的类型

获取索引或文档GET
通过GET请求获取具体的信息,后面跟索引名或者是id名都可以
1 | 简单查询 |

通过GET _cat/xxx 命令可以获取系统中的其它信息

修改(两种方式)
直接通过 PUT 方式进行修改
1
2
3
4
5PUT /test1/type1/1
{
"name": "袁凯强1",
"age": 21
}
通过 POST 方式进行修改(推荐使用)
1
2
3
4
5
6
7POST /test1/type1/1/_update
{
"doc": {
"name": "袁凯强12",
"age":99
}
}
删除DELETE
1 | DELETE test2 |

复杂操作搜索select(排序、分页、高亮、模糊查询、精准查询)
1 | GET /test1/_search |

查询:query
1
2
3
4
5
6
7
8GET /test1/_search
{
"query": {
"match": {
"name": "袁"
}
}
}
结果过滤 :_source
1
"_source": ["name"]

排序:sort(根据数值类型进行排序)
1
2
3
4
5
6
7"sort": [
{
"age": {
"order": "desc"
}
}
]
分页:from、size
1
2"from": 0,
"size": 1
布尔值查询:bool
1
2
3
4
5
6
7
8
9
10
11
12
13
14"bool": {
"must": [
{
"match": {
"name": "袁凯强"
}
},
{
"match": {
"age": 21
}
}
]
}must 相当于mysql中条件中的and

should 相当于mysql中条件中的or

must_not 相当于mysql中条件中的not

过滤:filter
1
2
3
4
5
6
7
8
9"filter": [
{
"range": {
"age": {
"gte": 20
}
}
}
]eq相等,ne、neq不相等,gt大于,lt小于,gte、ge大于等于,lte、le 小于等于,not非,mod求模,div by是否能被某数整除,even是否为偶数,odd是否为奇

匹配多个条件进行查询match
1
2
3
4
5"query": {
"match": {
"name": "袁 张"
}
}
精确查询:term,term查询是直接通过倒排索引指定的词条进程精确查找的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49PUT test2
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"desc": {
"type": "keyword"
}
}
}
}
PUT test2/_doc/1
{
"name": "袁凯强hello",
"desc": "注释"
}
PUT test2/_doc/2
{
"name": "袁凯强hello",
"desc": "注释2"
}
GET _analyze
{
"analyzer": "keyword",
"text": "袁凯强Hello"
}
GET _analyze
{
"analyzer": "standard",
"text": "袁凯强Hello"
}
GET /test2/_search
{
"query": {
"term": {
"name": "袁"
}
}
}
GET /test2/_search
{
"query": {
"term": {
"desc": "注释"
}
}
}使用es默认的分词器keyword与standard
keyword不会被拆分

standard会被拆分

因为name与desc中的字段类型不一样,keyword不能被分词器解析

name:text类型(trem+text分词查询=》通过standard解析)

desc:keyword类型(trem+keyword精确查询=》通过keyword解析)

多个值匹配精确查询

高亮查询:highlight
1 | "highlight":{ |

自定义高亮的标签,默认为em

6、集成SpringBoot
API官方文档 [^5]
API在线文档 [^6]


maven依赖
1 | <dependency> |
使用的对象

导入依赖时,保证es版本与系统版本的一致

精确查找时,不想被分词器解析
- 名称上加上keyword:keyword参考 [^7]

定义的时候加上定义的类型
@field注解参考 [^8]
@Document注解参考 [^9]
1 | @Field(type=FieldType.Text, analyzer="ik_max_word") 表示该字段是一个文本,并作最大程度拆分,默认建立索引 |

使用类似于JPA的形式使用elasticsearchanalyzer和search_analyzer的区别
Elasticsearch中analyzer和search_analyzer的区别 [^10]
1 | analyzer和search_analyzer的区别 |
使用类似于JPA的形式使用elasticsearch