一、Elasticsearch是什么?
- 定位 :基于 Lucene 的分布式、RESTful 搜索引擎和分析引擎。
- 核心功能 :快速全文检索、结构化搜索、数据分析、实时日志处理等。
- 适用场景 :搜索引擎、日志分析(如 ELK Stack)、大数据实时分析、商业智能(BI)等。
二、核心概念
1、 索引(index)
1. 定义
类比 :类似关系型数据库中的“表”,但设计更灵活,适用于非结构化数据。
作用 :存储具有相似结构的文档集合,是数据管理的顶层逻辑单元。
2. 核心特性
- Mapping(映射)
定义字段类型(如 text、keyword、integer、geo_point)。
动态映射 (Dynamic Mapping):ES 自动推断字段类型(例如 "123" 可能被识别为 text 而非 integer)。
显式映射 (Explicit Mapping):手动定义字段类型和属性(推荐生产环境使用)。
创建索引:
PUT /my_index
{
"mappings": {
"properties": {
"title": { "type": "text" }, // 全文搜索字段
"price": { "type": "float" }, // 数值类型,支持过滤和聚合
"tags": { "type": "keyword" } // 精确值匹配(如过滤)
}
}
}
- Settings(设置)
number_of_shards:主分片数量(索引创建后不可修改)。
number_of_replicas:每个主分片的副本数量(可动态调整)。
PUT /my_index
{
"settings": {
"number_of_shards": 3, // 将索引分为 3 个主分片
"number_of_replicas": 1 // 每个主分片有 1 个副本
}
}
3. 注意事项
- 分片数规划:分片过少无法扩展写入,分片过多影响性能(建议单分片 10GB~50GB)。
- 冷热分离:通过索引生命周期管理(ILM)将旧索引迁移到低性能节点。
2、文档(Document)
1. 定义
类比:类似数据库中的“一行记录”,是ES中存储和操作的基本单元。
格式:JSON对象,包含业务数据和元数据。
2. 核心特性
- 文档必须有_id(可自动生成或手动指定)
- 元数据字段:
- _index:所在索引的名称
- _type:已废弃(ES 7后默认为 _doc)
- _source:存储原始 JSON,支持检索原始内容
- _version:版本号,用于乐观锁控制
PUT /my_index/_doc/1 // 手动指定 _id=1
{
"title": "Elasticsearch Guide",
"price": 49.99,
"tags": ["search", "database"]
}
3. 操作类型
- Create :指定 _id 写入新文档(若 _id 存在则报错)。
- Index :覆盖写入(不存在则创建,存在则替换)。
- Update :局部更新(部分字段修改)。
- Delete:删除文档
3、节点(Node)与集群(Cluster)
1. 节点(Node)
- 定义 :单个 ES 服务实例,通常是物理机或虚拟机上的一个进程。
- 核心角色 :
- 主节点(Master Node) :管理集群状态(如索引增删、节点加入)、不处理用户请求。
- 数据节点(Data Node) :存储分片数据、执行 CRUD 和搜索操作。
- 协调节点(Coordinating Node) :接收客户端请求,将请求路由到相关节点并聚合结果(默认所有节点均为此角色)。
- 专用节点 :如 Ingest Node(数据预处理)、Machine Learning Node。
# 配置文件 elasticsearch.yml
node.master: true # 是否可作为主节点
node.data: false # 不作为数据节点(专用主节点)
2. 集群(Cluster)
- 定义 :多个节点协同工作的集合,对外提供统一的搜索服务。
- 核心特性 :
- 高可用 :节点故障时,副本分片自动晋升为主分片。
- 负载均衡 :请求由协调节点分发到各数据节点。
- 发现机制 :节点通过 Zen Discovery 或 gossip 协议(7.x 后版本支持)自动加入集群。
4、分片(Shard)与副本(Replica)
1. 分片(Shard)
- 定义 :索引被水平切分为多个子集,每个分片是一个独立的 Lucene 索引。
- 核心作用 :
- 横向扩展 :分片可分布在多个节点上,利用多机资源提升性能。
- 并行处理 :搜索和聚合操作在各分片上并行执行。
2. 副本(Replica)
- 定义 :主分片的完整拷贝,仅用于读取操作(不可写入)。
- 核心作用 :
- 高可用 :主分片故障时,副本分片自动接管。
- 负载均衡 :查询请求可路由到主分片或副本,提升吞吐量。
3. 配置实例
PUT /my_index
{
"settings": {
"number_of_shards": 3, // 主分片数为 3
"number_of_replicas": 1 // 每个主分片有 1 个副本
}
}
- 分片分配逻辑 :
- 假设集群有 2 个数据节点,3 个主分片 + 3 个副本分片会被分配如下:
Node1: Shard0 (Primary), Shard1 (Replica), Shard2 (Replica) Node2: Shard1 (Primary), Shard2 (Primary), Shard0 (Replica)
- 若新增节点,ES 会自动调整分片分布以均衡负载。
4. 注意事项
- 分片数 :创建索引时需合理规划,后续无法修改(需通过 Reindex API 迁移数据)。
- 副本数 :可动态调整,例如临时关闭副本以提升写入性能:
PUT /my_index/_settings
{
"index.number_of_replicas": 0
}
5、映射(Mapping)与动态特性
1. 映射的作用
定义索引中每个字段的数据类型和属性(如是否允许全文搜索、是否参与聚合)。
2. 动态映射的潜在问题
类型错误 :例如将 "2023-10-01" 自动推断为 text 而非 date,导致无法进行时间范围查询。
解决方案 :提前定义显式映射(尤其是生产环境)。
3. 核心字段类型
text:全文检索字段(会被分词,如“Quick Brown”分为 Quick 和 Brown)。
keyword:精确值字段(用于过滤、聚合、排序,如 status 值 published)。
nested:嵌套对象(解决数组字段的扁平化问题)。
三、架构及原理
1、 近实时(NRT,Near Real-Time)
核心机制 :
- 写入流程 :文档并非直接写入磁盘,而是先存到内存的 Buffer 中,随后写入 Translog (事务日志,用于故障恢复)。
- Refresh 操作 :Buffer 按默认间隔 1秒 将数据刷新到 Lucene 的 Segment (不可变的索引段),此时文档可被检索。
- Flush 操作 :定期(默认每 30 分钟或 Translog 大小达到阈值)将 Segment 写入磁盘并清空 Translog。
NRT 的意义 :
- 权衡 写入速度 与 数据可见性 :内存 Buffer 减少磁盘 I/O,1秒 延迟是可配置的(通过 refresh_interval 参数)。
- 潜在问题 :在 Refresh 前发生宕机时,依赖 Translog 恢复未持久化的数据。
配置示例 :
# 修改索引的刷新间隔为 5 秒
PUT /my_index/_settings
{
"index": { "refresh_interval": "5s" }
}
2、 倒排索引(Inverted Index)
核心结构 :
- 正排索引(Forward Index) :传统数据库模式,通过 ID 找到文档内容。
- 倒排索引(Inverted Index) :
- 词项(Term) :分词后的最小单元(如 "elastic"、"search")。
- 映射表(Postings List) :记录词项出现过的文档 ID(如 "elastic" → [Doc1, Doc3])。
- 词频(TF)与位置(Position) :支持相关性评分和短语查询。
高效搜索的原因 :
- 直接通过词项定位文档,避免全表扫描。
- 结合 压缩算法 (如 FST)减少内存占用,提升检索速度。
示例
假设两篇文档:
- Doc1: "Elasticsearch is fast"
- Doc2: "Search is powerful"
倒排索引生成:
Term | Documents
elastic → [Doc1]
search → [Doc1, Doc2]
fast → [Doc1]
powerful → [Doc2]
3、 分布式协调
1. 节点角色 :
- Master Node :负责集群状态管理(如索引创建/删除、节点加入/离开)。
- Data Node :存储分片数据,执行 CRUD 和搜索操作。
- Coordinating Node (默认所有节点):接受客户端请求并路由到正确节点。
2. 集群发现机制(Zen Discovery) :
- 早期版本 :基于 Gossip 协议和选举算法(如 Bully Algorithm)。
- 7.x 版本后 :改用 Raft 协议 ,提升选举效率和一致性。
- 分片分配策略 :
主分片 (Primary Shard)负责写入,副本分片(Replica)同步数据。
默认动态均衡分片位置,避免单点故障。
3. 故障恢复流程 :
- 节点宕机:Master 节点检测到节点失联(默认 3 次 ping 失败)。
- 分片重新分配:将宕机节点的分片副本提升为主分片,或在其他节点重建副本。
四、核心功能
1、全文检索
get /my_index/_search
1. match查询
match 查询是 Elasticsearch 中最常用的查询类型之一,它会根据查询条件查找包含关键词的文档。
{
"query": {
"match": {
"field_name": "search_term"
}
}
}
- 示例:查询 title 字段包含关键词 “Elasticsearch” 的文档:
{
"query": {
"match": {
"title": "Elasticsearch"
}
}
}
2. Term 查询
term 查询用于精确匹配,不会对查询词进行分析(不进行分词处理),适用于数字、关键词等字段。
{
"query": {
"term": {
"field_name": "exact_value"
}
}
}
- 示例:查询 status 字段为 active 的文档:
{
"query": {
"term": {
"status": "active"
}
}
}
3. Range 查询
range 查询用于查找在指定范围内的文档,通常用于数值、日期等字段。
{
"query": {
"range": {
"field_name": {
"gte": "lower_bound",
"lte": "upper_bound"
}
}
}
}
- 示例:查询 age 字段在 20 到 30 之间的文档:
{
"query": {
"range": {
"age": {
"gte": 20,
"lte": 30
}
}
}
}
4. Bool查询
bool 查询用于组合多个查询条件,可以使用 must(必须满足)、should(可以满足)、must_not(不能满足)等逻辑操作符。
{
"query": {
"bool": {
"must": [
{ "match": { "field_name1": "value1" } },
{ "match": { "field_name2": "value2" } }
],
"filter": [
{ "range": { "field_name3": { "gte": "value3" } } }
]
}
}
}
- 示例:查询 title 包含 “Elasticsearch” 且 age 大于 25 的文档:
{
"query": {
"bool": {
"must": [
{ "match": { "title": "Elasticsearch" } }
],
"filter": [
{ "range": { "age": { "gte": 25 } } }
]
}
}
}
5. Wildcard 查询
wildcard查询用于基于模式匹配的搜索,可以使用*来代表任意字符,?代表一个字符。
{
"query": {
"wildcard": {
"field_name": "pattern"
}
}
}
- 示例:查询 username 字段以 “user” 开头的文档:
{
"query": {
"wildcard": {
"username": "user*"
}
}
}
6. Fuzzy查询
fuzzy 查询用于执行模糊匹配,适用于搜索具有相似拼写的词。
{
"query": {
"fuzzy": {
"field_name": {
"value": "search_term",
"fuzziness": "AUTO"
}
}
}
}
- 示例:查询 name 字段模糊匹配 “Elasitcsearch” 的文档:
{
"query": {
"fuzzy": {
"name": {
"value": "Elasitcsearch",
"fuzziness": "AUTO"
}
}
}
}
7. Match All 查询
match_all 查询会返回索引中的所有文档,通常用于获取所有数据。
{
"query": {
"match_all": {}
}
}
8. Highlight 查询
highlight 用于高亮显示查询匹配的部分,在查询结果中突出显示相关字段。
{
"query": {
"match": {
"field_name": "search_term"
}
},
"highlight": {
"fields": {
"field_name": {}
}
}
}
9. Prefix 查询
prefix 查询用于查找字段值以特定前缀开头的文档。
{
"query": {
"prefix": {
"field_name": "prefix_value"
}
}
}
2、Aggregation(聚合)分析
指标计算(Metrics Aggregations)
聚合用于对数据进行分组或汇总,常用于统计分析。
avg:计算某个字段的平均值
GET /products/_search
{
"size": 0,
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
解释:计算 price 字段的平均值。
sum:计算某个字段的总和
GET /products/_search
{
"size": 0,
"aggs": {
"total_sales": {
"sum": {
"field": "sales"
}
}
}
}
解释:计算 sales 字段的总和。
max / min:计算某个字段的最大值/最小值
GET /products/_search
{
"size": 0,
"aggs": {
"max_price": {
"max": {
"field": "price"
}
},
"min_price": {
"min": {
"field": "price"
}
}
}
}
解释:分别计算 price 字段的最大值和最小值。
cardinality:计算某个字段的去重值数量
GET /products/_search
{
"size": 0,
"aggs": {
"unique_categories": {
"cardinality": {
"field": "category"
}
}
}
}
解释:计算 category 字段中不同(去重后的)值的数量,相当于 SQL 中的 COUNT(DISTINCT)。
分组(Bucket Aggregations)
这些聚合根据某个字段的值将文档分成不同的“桶”,如按类别、时间或数值范围进行分组。
terms:按字段值分组
GET /products/_search
{
"size": 0,
"aggs": {
"category_groups": {
"terms": {
"field": "category"
}
}
}
}
解释:将文档按 category 字段的值分组,返回每个类别及其文档数量。
date_histogram:按时间间隔分组(如按天分组)
GET /products/_search
{
"size": 0,
"aggs": {
"daily_sales": {
"date_histogram": {
"field": "timestamp",
"calendar_interval": "day"
},
"aggs": {
"total_sales": {
"sum": {
"field": "sales"
}
}
}
}
}
}
解释:将文档按 timestamp 字段按天分组,并计算每一天的 sales 总和。
range:按数值范围分组
GET /products/_search
{
"size": 0,
"aggs": {
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 100 },
{ "from": 100, "to": 500 },
{ "from": 500 }
]
}
}
}
}
解释:将文档根据 price 字段的值分为三个区间:小于 100、100 到 500 之间、大于 500。
管道聚合(Pipeline Aggregations)
作用 :基于其他聚合结果进一步计算(如计算每个月的增长率)。
derivative:计算导数(如计算增长率)
GET /products/_search
{
"size": 0,
"aggs": {
"daily_sales": {
"date_histogram": {
"field": "timestamp",
"calendar_interval": "day"
},
"aggs": {
"total_sales": {
"sum": {
"field": "sales"
}
},
"sales_derivative": {
"derivative": {
"buckets_path": "total_sales"
}
}
}
}
}
}
解释:先按天分组计算每一天的 sales 总和,然后计算每天销售总和的增长(即每一天与前一天的差异,类似于增长率)。
moving_avg:计算移动平均
GET /products/_search
{
"size": 0,
"aggs": {
"daily_sales": {
"date_histogram": {
"field": "timestamp",
"calendar_interval": "day"
},
"aggs": {
"total_sales": {
"sum": {
"field": "sales"
}
},
"sales_moving_avg": {
"moving_avg": {
"buckets_path": "total_sales",
"window": 7,
"model": "simple"
}
}
}
}
}
}
解释:先按天分组计算 sales 总和,然后计算该总和的 7 天简单移动平均。
bucket_script:跨桶计算
GET /products/_search
{
"size": 0,
"aggs": {
"category_groups": {
"terms": {
"field": "category"
},
"aggs": {
"average_price": {
"avg": {
"field": "price"
}
},
"price_to_sales_ratio": {
"bucket_script": {
"buckets_path": {
"avg_price": "average_price",
"total_sales": "total_sales"
},
"script": "params.avg_price / params.total_sales"
}
}
}
}
}
}
解释:在每个类别下,首先计算 price 字段的平均值,然后通过 bucket_script 聚合计算价格与销售总和的比值(比如价格与销售的比率)。
评论区