Elasticsearch
Elasticsearch 是基于 Lucene 的分布式全文搜索引擎,以 JSON 格式存储数据,通过 RESTful API 操作。核心特点:近实时搜索、水平扩展、高可用。
适用场景:日志分析(ELK Stack)、商品搜索与全文检索、应用性能监控(APM)、安全日志分析与威胁检测。当数据量达到百万级以上且需要复杂检索和聚合分析时,Elasticsearch 优于传统关系型数据库的 LIKE 查询。
核心要点
分布式全文搜索引擎核心知识,包括倒排索引、集群架构、查询DSL
核心概念
| 概念 | 说明 |
|---|---|
| 倒排索引 | 将词条映射到文档,实现高效的全文搜索 |
| 分布式架构 | 数据分片存储,自动故障转移,保证高可用 |
| RESTful API | 通过 HTTP 接口操作,支持增删改查、聚合分析 |
| Near Real-Time | 数据写入后约 1 秒即可被检索 |
| 分片(Shard) | 数据分片单元,每个分片是独立的 Lucene 索引 |
| 副本(Replica) | 分片的备份,提高可用性和读取吞吐量 |
倒排索引原理
| 方式 | 说明 |
|---|---|
| 正向索引 | 文档 → 遍历 → 找到关键词(O(n)) |
| 倒排索引 | 词 → 文档映射表 → O(1) 检索 |
倒排索引 = 词典 + 倒排表,从词出发,记载词在哪些文档中出现过。
Lucene 4+ 使用 FST(Finite State Transducer,有限状态转换器) 数据结构:一种将前缀和后缀共享压缩的字典树变体,空间占用小,查询速度快 O(len(str))。
节点角色
| 角色 | 说明 |
|---|---|
| Master | 集群、节点、索引管理(不负责文档级别管理) |
| Data | 存储数据,参与搜索和聚合 |
| Coordinating | 接收请求,转发到其他节点 |
| Ingest | 数据预处理 |
集群架构
典型架构设计
| 配置项 | 建议值 |
|---|---|
| 节点数量 | 按业务需求,通常 3-10 个 |
| 分片数量 | 每索引 3-5 个主分片 |
| 副本数量 | 1-2 个(高可用场景) |
| 单索引大小 | 建议控制在 150GB 以内 |
索引调优手段
设计阶段:
- 日期模板 + rollover API 自动滚动创建新索引
- 别名管理便于切换索引
- force_merge 每日凌晨合并段,释放空间
- 冷热分离:热数据存 SSD,冷数据定期 shrink
- 合理选择分词器
写入阶段:
- 写入前关闭副本,加快写入
- 关闭 refresh_interval
- bulk 批量写入提高吞吐量
- 写入完成后恢复副本数
查询阶段:
- 禁用 wildcard 避免全量扫描
- 充分利用倒排索引
- 先时间后检索减少搜索范围
- 合理路由机制直接定位分片
Master 选举
选举原理
只有候选主节点(master: true)才能成为主节点,minimum_master_nodes 防止脑裂。
选举流程:所有 master 候选节点按 nodeId 字典排序,选出第0位,投票数达到 n/2+1 成为 Master。
脑裂问题
脑裂(Split Brain)是指网络分区导致集群中出现多个 Master 节点,各自接受写请求,造成数据不一致。
| 场景 | 解决方案 |
|---|---|
| master 候选 ≥ 3 | 设置 discovery.zen.minimum_master_nodes 超过一半 |
| master 候选 = 2 | 只保留一个候选,其他作为 data 节点 |
文档写入流程
索引文档流程
- 协调节点接收请求
- 计算分片路由:
shard = hash(_routing) % num_of_primary_shards - 转发到主分片节点
- 主分片写入
- 并行转发副本分片
- 副本都成功后返回成功
写入原理
| 阶段 | 说明 |
|---|---|
| Memory Buffer | 接收请求写入内存 |
| Filesystem Cache | 每隔 1 秒 refresh 生成新段 |
| translog | 保证数据可靠性(先写日志) |
| flush | 写入磁盘,清除 translog |
触发 flush 时机:定时触发(默认 30 分钟)或 translog 太大(默认 512M)。
搜索流程
Query Then Fetch
Query 阶段:
- 查询广播到所有分片拷贝
- 每个分片本地执行,构建优先队列
- 返回文档 ID 和排序值到协调节点
- 协调节点合并产生全局排序列表
Fetch 阶段:
- 协调节点向相关分片提交 GET 请求
- 分片加载并丰富文档
- 返回文档给协调节点
DFS Query Then Fetch 预查询 Term 和 Document frequency,评分更准确,但性能较差。
更新和删除
Elasticsearch 文档是不可变的,更新实际上是写入新文档 + 删除旧文档:
| 操作 | 原理 |
|---|---|
| 删除 | 文档不真正删除,在 .del 文件中标记删除,段合并时真正删除 |
| 更新 | 新版本文档写入新段,旧版本在 .del 中标记删除 |
Segment 机制
索引由多个段组成,段本身是不可变的倒排索引。新文档增量添加到索引,段越多搜索性能越低。
合并策略:定期合并小段为大段,减少段数量,提高搜索性能,合并时真正删除已标记的文档。
并发一致性
写操作
支持乐观并发控制(版本号),一致性级别:quorum / one / all
读操作
| 模式 | 说明 |
|---|---|
| sync(默认) | 同步复制,等所有副本完成 |
| async | 异步复制,性能更高 |
| primary | 只读主分片 |
数据结构
字典树(Trie)
- 根节点不包含字符
- 每个节点只包含一个字符
- 从根到某节点路径 = 该节点对应字符串
- 中文 Trie 子节点用哈希表存储
拼写纠错
基于编辑距离(插入、删除、替换操作的最小步数)和 BK 树实现相似词查询。
部署优化
| 优化项 | 建议 |
|---|---|
| 内存 | 64GB 理想,32GB 也可,最少 8GB |
| CPU | 多核优于高频率 |
| 磁盘 | SSD 优于机械磁盘 |
| 集群 | 避免跨数据中心 |
| 内存分配 | Lucene 使用一半内存(不超过 32GB) |
| Swap | 关闭交换分区 |
| 文件描述符 | 设置 64000+ |
| 发现 | 使用单播代替组播 |
大数据聚合
使用 cardinality 度量(基数),基于 HLL 算法(HyperLogLog):
- 可配置精度控制内存使用
- 内存使用量与精确度相关,与数据量无关
数据模型
索引结构
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"title": { "type": "text" },
"content": { "type": "text" },
"created_at": { "type": "date" }
}
}
}字段类型
| 类型 | 说明 |
|---|---|
| text | 全文检索,支持分词 |
| keyword | 精确值,不分词 |
| integer/long | 数值类型 |
| boolean | 布尔类型 |
| date | 日期类型 |
| nested | 嵌套对象 |
| geo_point | 地理位置 |
查询 DSL
全文查询
{ "query": { "match": { "title": "elasticsearch" } } }精确查询
{ "query": { "term": { "status": "published" } } }复合查询
{
"query": {
"bool": {
"must": [{ "match": { "title": "elasticsearch" } }],
"filter": [{ "term": { "status": "published" } }]
}
}
}聚合分析
{
"aggs": {
"status_count": {
"terms": { "field": "status" }
}
}
}主要应用场景
| 场景 | 说明 |
|---|---|
| 日志分析 | ELK Stack(Elasticsearch + Logstash + Kibana) |
| 全文检索 | 商品搜索、内容搜索 |
| 性能监控 | 应用性能监控、基础设施监控 |
| 安全分析 | 安全日志分析、威胁检测 |
常见问题
分片状态异常
集群健康状态为 yellow 表示副本分片未分配(单节点常见),red 表示主分片未分配(数据可能丢失)。使用 GET _cluster/health 查看状态,GET _cat/shards?v 定位异常分片。
查询性能下降
- 使用
GET _nodes/hot_threads查看热点线程 - 检查是否触发了 wildcard(
*keyword*)全量扫描 - 确认查询是否命中了合适的索引,使用
GET /index_name/_validate/query?explain验证
磁盘空间不足
- 执行
POST /index_name/_forcemerge?max_num_segments=1合并段释放空间 - 使用 ILM(Index Lifecycle Management)自动管理索引生命周期
- 冷数据定期 shrink 或 rollto 到低成本存储