一、Elasticsearch 基础概念
1. 定位与特点
- 分布式搜索引擎:基于 Lucene 构建,提供分布式、高扩展、高可用的实时搜索与数据分析能力。
- 核心优势:
- 分布式架构,支持水平扩展(节点自动发现、负载均衡)。
- 实时索引与搜索,数据写入后可被立即搜索。
- 灵活的数据建模(JSON 文档存储,动态映射)。
- 强大的聚合分析功能,支持复杂查询(如布尔查询、模糊查询、范围查询等)。
2. 核心术语
术语 | 含义描述 |
---|---|
Cluster | 集群,由一个或多个节点组成,共同存储数据并提供搜索服务。 |
Node | 集群中的单个服务器实例,负责存储数据、处理请求。 |
Index | 索引,逻辑上的数据集(类似关系型数据库中的 “数据库”),包含多个文档。 |
Type | 旧版本(7.x 前)用于区分索引内不同类别的文档,7.x 后废弃,默认唯一类型为 _doc 。 |
Document | 索引中的单个数据单元(类似关系型数据库中的 “行”),以 JSON 格式存储。 |
Field | 文档中的字段(类似关系型数据库中的 “列”),每个字段有特定的数据类型。 |
Shard | 索引的物理分片,一个索引可拆分为多个分片,分布在不同节点以实现分布式存储。 |
Replica | 分片的副本,用于高可用性(故障恢复)和读扩展(分担查询压力)。 |
二、核心架构与原理
1. 分布式架构
- 节点类型:
- 主节点(Master Node):负责集群状态管理(如创建索引、分配分片、追踪节点)。
- 数据节点(Data Node):存储数据,处理数据相关操作(索引、搜索、聚合)。
- 协调节点(Coordinating Node):路由请求,合并多个节点的查询结果(默认每个节点都是协调节点)。
- 预处理节点(Ingest Node):对文档进行预处理(如数据转换、 enrichment)。
- 分片机制:
- 主分片(Primary Shard):负责数据写入和读取,数量在索引创建时固定(默认 1 个)。
- 副本分片(Replica Shard):主分片的副本,可在节点间动态分配。
- 分片策略:通过哈希算法(如文档 ID 哈希)将数据分配到不同主分片,确保数据均衡。
2. 索引与搜索流程
- 索引流程:
- 文档被写入某个主分片,同步到副本分片。
- 数据先写入内存缓冲区(Buffer),定时生成 Segment(分段)并刷新到磁盘(Refresh 机制)。
- 多个 Segment 定期合并(Merge),减少磁盘 I/O 开销。
- 搜索流程:
- 协调节点将查询请求分发到相关分片(主分片或副本分片)。
- 各分片执行查询并返回结果(命中文档的 ID 和评分)。
- 协调节点合并结果,按评分排序后返回给客户端。
3. 倒排索引(Inverted Index)
- 核心原理:Lucene 的底层实现,将文档中的关键词映射到包含该词的文档列表,用于快速检索。
- 结构示例:
- 关键词 "elastic" → 文档列表 [Doc1, Doc3]
- 关键词 "search" → 文档列表 [Doc1, Doc2]
- 优势:通过分词、归一化(如小写、去重)等预处理,实现高效全文搜索。
三、常用操作与 API
1. 索引操作
- 创建索引:
json
PUT /my_index { "settings": { "number_of_shards": 3, // 主分片数 "number_of_replicas": 1 // 副本数 }, "mappings": { "properties": { "title": {"type": "text"}, // 文本类型(可分词) "price": {"type": "integer"} // 数值类型 } } }
- 查看索引:
json
GET /my_index
- 删除索引:
json
DELETE /my_index
2. 文档操作
- 新增 / 更新文档:
- 按 ID 新增(若 ID 不存在则创建,存在则覆盖):
json
PUT /my_index/_doc/1 { "title": "Elasticsearch Guide", "price": 99 }
- 自动生成 ID:
json
POST /my_index/_doc { "title": "Elasticsearch Basics", "price": 59 }
- 按 ID 新增(若 ID 不存在则创建,存在则覆盖):
- 查询文档:
json
GET /my_index/_doc/1
- 删除文档:
json
DELETE /my_index/_doc/1
3. 搜索与聚合查询
- 简单搜索(Match Query):
json
GET /my_index/_search { "query": { "match": { "title": "elasticsearch" } } }
- 布尔查询(Bool Query):组合多个查询条件(must/filter/should/must_not):
json
GET /my_index/_search { "query": { "bool": { "must": { "match": { "title": "search" } }, "filter": { "range": { "price": { "lte": 100 } } } } } }
- 聚合分析(Aggregation):
json
GET /my_index/_search { "aggs": { "avg_price": { "avg": { "field": "price" } }, // 计算平均价格 "price_stats": { "stats": { "field": "price" } } // 统计最大值、最小值等 } }
四、数据建模与 Mapping
1. 数据类型
- 核心类型:
- 文本类型(Text):可分词,用于全文搜索(如标题、内容)。
- 关键字类型(Keyword):不可分词,用于精确匹配(如 ID、标签)。
- 数值类型(Number):支持整数、浮点数(如
integer
、double
)。 - 日期类型(Date):存储日期时间(如
2023-10-01
或时间戳)。 - 二进制类型(Binary):存储二进制数据(如文件内容,需配合 Base64 编码)。
- 复杂类型:
- 对象类型(Object):嵌套文档(类似 JSON 对象)。
- 数组类型(Array):同一字段存储多个值(如
[1, 2, 3]
或["apple", "banana"]
)。
2. 动态映射(Dynamic Mapping)
- 自动创建字段:当写入文档时,若字段未定义,ES 会根据数据类型自动生成 Mapping。
- 控制动态映射:通过
dynamic
参数禁用自动映射:json
PUT /my_index { "mappings": { "dynamic": "strict" // 禁止自动创建新字段,写入未知字段会报错 } }
3. 分词器(Analyzer)
- 作用:将文本拆分为 tokens(词元),用于倒排索引构建。
- 内置分词器:
- 标准分词器(Standard Analyzer):按词边界拆分(默认,适合英文)。
- 中文分词器:需安装插件(如
ik-analyzer
),支持按词语拆分(如 “Elasticsearch 教程”→ ["Elasticsearch", "教程"])。
- 自定义分词器:组合字符过滤器、分词器、词元过滤器:
json
PUT /my_index { "settings": { "analysis": { "analyzer": { "my_analyzer": { "type": "custom", "tokenizer": "standard", "filter": ["lowercase"] // 转换为小写 } } } } }
五、集群管理与高可用性
1. 集群发现与节点配置
- 单播(Unicast):手动指定可连接的节点列表(默认方式,避免广播风暴):
yaml
# elasticsearch.yml 配置 cluster.name: my-es-cluster node.name: node-1 network.host: 0.0.0.0 discovery.seed_hosts: ["node-1:9300", "node-2:9300"] # 种子节点
- 自动发现:通过云服务(如 AWS、阿里云)或 Kubernetes 实现节点自动加入集群。
2. 分片分配策略
- 默认策略:ES 自动平衡分片在节点间的分布,确保主分片和副本分片不位于同一节点。
- 自定义策略:通过节点属性(如
node.attr.rack
)和分片分配规则,控制分片分布:json
PUT /my_index/_settings { "index.routing.allocation.require.rack": "rack1" // 要求分片分配到属性为 rack1 的节点 }
3. 故障恢复与副本机制
- 高可用性保障:
- 当主节点故障时,集群自动选举新的主节点(基于 Quorum 机制,需超过半数节点存活)。
- 主分片故障时,副本分片自动升级为主分片,确保数据不丢失。
- 副本调整:动态增加或减少副本数:
json
PUT /my_index/_settings { "number_of_replicas": 2 // 调整副本数为 2 }
六、性能优化与调优
1. 硬件与配置优化
- 节点角色分离:主节点、数据节点、协调节点分离,避免资源竞争。
- 内存配置:
- 单个节点内存不超过 32GB(受限于 Lucene 的内存压缩机制)。
- 预留至少 50% 内存给操作系统文件缓存(Lucene 依赖磁盘缓存提升查询性能)。
- 磁盘选择:使用 SSD 存储热数据,HDD 存储冷数据(配合冷热节点架构)。
2. 索引优化
- 减少分片数量:单个分片建议大小为 10-50GB,避免过多分片导致集群状态维护开销。
- 预分配主分片:创建索引时指定合理的主分片数(如预计数据量 1TB,主分片数设为 20,每个分片约 50GB)。
- 禁用副本写入:写入大量数据时,先将副本数设为 0,写入完成后再恢复(减少网络传输开销):
json
PUT /my_index/_settings { "number_of_replicas": 0 }
3. 查询优化
- 优先使用 Filter 而非 Query:Filter 结果可缓存,性能更高(如范围查询、布尔查询中的 filter 子句)。
- 批量操作(Bulk API):通过一次请求批量处理多个文档(索引、删除等),减少网络往返次数:
json
POST /_bulk {"index":{"_index":"my_index","_id":"1"}} {"title":"Bulk Example 1","price":100} {"index":{"_index":"my_index","_id":"2"}} {"title":"Bulk Example 2","price":200}
- 避免深翻页(Deep Pagination):当查询结果超过数万条时,使用 Scroll API 或 Search After 替代
from + size
,避免内存溢出。
七、生态工具与集成
1. 数据摄入工具
- Logstash:用于日志收集、处理和传输(支持多种数据源,如文件、数据库、消息队列)。
- Filebeat:轻量级日志采集器,配合 Logstash 或直接写入 ES。
- Ingest Node Pipeline:在文档写入时通过预处理管道(Pipeline)实时转换数据(如提取字段、添加元数据)。
2. 可视化工具
- Kibana:ES 官方可视化工具,支持仪表盘、图表、日志分析、告警配置等。
- Grafana:与 ES 集成,用于监控数据可视化(需配置 ES 数据源)。
3. 客户端与集成
- 官方客户端:支持 Java、Python、JavaScript(Node.js)、Go 等语言。
- Spring Data Elasticsearch:Spring 框架集成 ES 的官方库,简化数据操作。
- SQL 接口:通过 Elasticsearch SQL 或 Apache Hive/Spark 插件,使用 SQL 查询 ES 数据。
八、高级主题
1. 冷热架构(Hot-Warm-Cold)
- 热节点:存储最近期数据,使用 SSD,支持高读写性能。
- 温节点:存储中期数据,使用 HDD,性能中等。
- 冷节点:存储历史数据,支持归档(如冻结合约),仅支持读取。
- 策略配置:通过 Time-Based Index(按时间创建索引)和 ILM(索引生命周期管理)自动迁移数据。
2. 跨集群复制(CCR)
- 作用:将一个集群(源集群)的数据实时复制到另一个集群(目标集群),用于灾备或读写分离。
- 配置示例:
json
PUT /_cluster/settings { "persistent": { "ccr": { "auto_follow": { "my_repo": { "source": "source_cluster", "remote_cluster": "remote_cluster" } } } } }
3. 安全与权限控制
- 内置安全特性(需 X-Pack 插件):
- 节点间 TLS 加密、客户端认证。
- 基于角色的访问控制(RBAC),控制用户对索引、文档的操作权限。
- 审计日志,记录用户操作。
- 配置示例:启用基本认证:
yaml
xpack.security.enabled: true xpack.security.authc.accept_default_password: true # 首次启动时生成默认密码
九、常见问题与排查
- 分片分配失败:
- 原因:磁盘空间不足、节点属性不匹配、副本与主分片在同一节点。
- 排查:通过
GET /_cluster/health?pretty
查看集群状态,GET /_cat/shards
检查分片分布。
- 查询性能慢:
- 原因:分片数过多、未使用合适的分词器、查询语句复杂。
- 优化:使用 Query Profiler 分析查询耗时,添加字段别名或索引。
- 集群脑裂(Split Brain):
- 原因:网络分区导致主节点选举冲突。
- 预防:设置
discovery.zen.minimum_master_nodes
为(master节点数/2)+1
,确保选举需要多数节点同意。