ElasticSearch 深度解析

ElasticSearch 是基于 Lucene 的分布式搜索引擎,以其强大的全文检索、实时分析和可扩展性著称。本文将深入剖析 ElasticSearch 的核心概念、查询语法、聚合分析以及集群架构。

ElasticSearch 简介

ElasticSearch(简称 ES)是一个基于 Lucene 的分布式、RESTful 风格的搜索和数据分析引擎,是 ELK(Elasticsearch、Logstash、Kibana)技术栈的核心组件。

ElasticSearch 的核心特性:

  • 分布式架构:支持水平扩展,自动分片和复制
  • 实时性:近实时搜索,数据写入后 1 秒内可搜索
  • 全文检索:基于 Lucene,支持复杂的全文搜索
  • RESTful API:提供简洁的 HTTP 接口
  • 多语言支持:支持 Java、Python、Go 等多种语言
  • 聚合分析:支持强大的数据分析能力
  • 高可用:支持故障自动转移

核心概念

Index(索引)

Index 是相似文档的集合,类似于关系数据库中的数据库。

# 创建索引
PUT /products

# 删除索引
DELETE /products

# 查看索引信息
GET /products

Type(类型)

Type 是索引内部的逻辑分类,类似于关系数据库中的表。注意:ES 7.x 之后已移除 Type 概念。

Document(文档)

Document 是索引中的基本数据单元,以 JSON 格式存储,类似于关系数据库中的行。

# 创建文档
PUT /products/_doc/1
{
  "name": "iPhone 15",
  "price": 5999,
  "category": "手机"
}

# 获取文档
GET /products/_doc/1

# 更新文档
POST /products/_update/1
{
  "doc": {
    "price": 5799
  }
}

# 删除文档
DELETE /products/_doc/1

Node(节点)

Node 是 ES 集群中的单个服务器,存储数据并参与集群的索引和搜索。

Cluster(集群)

Cluster 是一个或多个节点的集合,共享数据并提供索引和搜索能力。

Shard(分片)

Shard 是索引的水平分割,将大数据分散到多个节点。

  • 主分片(Primary Shard):每个文档都属于一个主分片
  • 副本分片(Replica Shard):主分片的副本,提供高可用

数据类型

核心数据类型

类型 说明 示例
text 全文检索字段 "hello world"
keyword 精确匹配字段 "iPhone 15"
integer 32位整数 123
long 64位整数 123456789
float 单精度浮点数 3.14
double 双精度浮点数 3.1415926
boolean 布尔值 true/false
date 日期时间 "2024-03-29"
binary 二进制数据 base64 编码

复杂数据类型

类型 说明 示例
object JSON 对象 {"name": "张三", "age": 25}
nested 嵌套对象 [{"name": "张三"}, {"name": "李四"}]
array 数组 [1, 2, 3]

地理数据类型

类型 说明 示例
geo_point 经纬度点 {"lat": 39.9, "lon": 116.4}
geo_shape 地理形状 多边形、圆形等

Mapping(映射)

Mapping 定义了索引中字段的类型和属性。

动态映射

ES 可以自动推断字段类型并创建映射。

# 关闭动态映射
PUT /products
{
  "mappings": {
    "dynamic": "strict"
  }
}

显式映射

# 创建索引时定义映射
PUT /products
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "price": {
        "type": "double"
      },
      "category": {
        "type": "keyword"
      },
      "created_at": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss"
      }
    }
  }
}

映射参数

参数 说明
index 是否索引
store 是否单独存储
analyzer 分词器
search_analyzer 搜索分词器
norms 是否评分
doc_values 是否启用文档值

分词器

内置分词器

分词器 说明
standard 标准分词器,按词分隔
simple 简单分词器,按非字母字符分隔
whitespace 按空白字符分隔
stop 去除停用词
keyword 不分词
pattern 正则表达式分词

中文分词器

常用的中文分词器有 IK、jieba、HanLP 等。

# 安装 IK 分词器
bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.0/elasticsearch-analysis-ik-7.17.0.zip

# 使用 IK 分词器
PUT /products
{
  "settings": {
    "analysis": {
      "analyzer": {
        "ik_max_word": {
          "type": "custom",
          "tokenizer": "ik_max_word"
        }
      }
    }
  }
}

自定义分词器

# 创建自定义分词器
PUT /products
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase", "my_stop"]
        },
        "filter": {
          "my_stop": {
            "type": "stop",
            "stopwords": ["the", "a", "an"]
          }
        }
      }
    }
  }
}

查询 DSL

基本查询

# 查询所有文档
GET /products/_search

# 分页查询
GET /products/_search
{
  "from": 0,
  "size": 10
}

# 排序
GET /products/_search
{
  "sort": [
    {"price": {"order": "desc"}}
  ]
}

全文查询

# match 查询(分词查询)
GET /products/_search
{
  "query": {
    "match": {
      "name": "iPhone 手机"
    }
  }
}

# match_phrase 查询(短语查询)
GET /products/_search
{
  "query": {
    "match_phrase": {
      "name": "iPhone 15"
    }
  }
}

# multi_match 查询(多字段查询)
GET /products/_search
{
  "query": {
    "multi_match": {
      "query": "iPhone",
      "fields": ["name", "description"]
    }
  }
}

# query_string 查询(Lucene 查询语法)
GET /products/_search
{
  "query": {
    "query_string": {
      "query": "name:iPhone AND price:[5000 TO 7000]"
    }
  }
}

精确查询

# term 查询(精确匹配)
GET /products/_search
{
  "query": {
    "term": {
      "category": "手机"
    }
  }
}

# terms 查询(多值匹配)
GET /products/_search
{
  "query": {
    "terms": {
      "category": ["手机", "电脑"]
    }
  }
}

# range 查询(范围查询)
GET /products/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 5000,
        "lte": 7000
      }
    }
  }
}

# exists 查询(字段存在)
GET /products/_search
{
  "query": {
    "exists": {
      "field": "description"
    }
  }
}

布尔查询

# bool 查询(组合查询)
GET /products/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {"name": "iPhone"}}
      ],
      "must_not": [
        {"term": {"category": "电脑"}}
      ],
      "should": [
        {"range": {"price": {"lte": 6000}}}
      ],
      "filter": [
        {"term": {"status": "available"}}
      ]
    }
  }
}

bool 查询参数:

  • must:必须匹配(AND)
  • must_not:必须不匹配(NOT)
  • should:应该匹配(OR)
  • filter:过滤(不评分)

聚合分析

指标聚合

# avg 聚合(平均值)
GET /products/_search
{
  "aggs": {
    "avg_price": {
      "avg": {
        "field": "price"
      }
    }
  }
}

# sum 聚合(求和)
GET /products/_search
{
  "aggs": {
    "total_sales": {
      "sum": {
        "field": "sales"
      }
    }
  }
}

# max/min 聚合(最大值/最小值)
GET /products/_search
{
  "aggs": {
    "max_price": {
      "max": {
        "field": "price"
      }
    },
    "min_price": {
      "min": {
        "field": "price"
      }
    }
  }
}

# stats 聚合(统计信息)
GET /products/_search
{
  "aggs": {
    "price_stats": {
      "stats": {
        "field": "price"
      }
    }
  }
}

桶聚合

# terms 聚合(分组)
GET /products/_search
{
  "aggs": {
    "by_category": {
      "terms": {
        "field": "category",
        "size": 10
      }
    }
  }
}

# range 聚合(范围分组)
GET /products/_search
{
  "aggs": {
    "price_ranges": {
      "range": {
        "field": "price",
        "ranges": [
          {"to": 3000},
          {"from": 3000, "to": 5000},
          {"from": 5000}
        ]
      }
    }
  }
}

# date_histogram 聚合(时间分组)
GET /orders/_search
{
  "aggs": {
    "sales_over_time": {
      "date_histogram": {
        "field": "created_at",
        "calendar_interval": "day"
      }
    }
  }
}

# histogram 聚合(数值分组)
GET /products/_search
{
  "aggs": {
    "price_histogram": {
      "histogram": {
        "field": "price",
        "interval": 1000
      }
    }
  }
}

嵌套聚合

# 多层聚合
GET /products/_search
{
  "aggs": {
    "by_category": {
      "terms": {
        "field": "category"
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"
          }
        },
        "by_brand": {
          "terms": {
            "field": "brand"
          }
        }
      }
    }
  }
}

集群架构

节点类型

节点类型 说明
master-eligible 有资格成为主节点
data 数据节点,存储数据
coordinating 协调节点,处理请求
ingest 预处理节点
machine_learning 机器学习节点

分片策略

# 创建索引时指定分片数
PUT /products
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  }
}

分片数选择原则:

  • 单个分片大小建议 10-50GB
  • 分片数过多会影响性能
  • 分片数过少无法充分利用集群资源

集群健康

# 查看集群健康状态
GET /_cluster/health

# 健康状态:
# green:所有主分片和副本分片都正常
# yellow:所有主分片正常,部分副本分片不正常
# red:部分主分片不正常

性能优化

索引优化

  • 合理设置分片数:根据数据量设置合适的分片数
  • 使用 _bulk 批量操作:减少网络开销
  • 禁用不需要的字段:减少索引大小
  • 使用 refresh_interval:调整刷新频率
# 批量索引
POST /products/_bulk
{ "index": { "_id": 1 }}
{ "name": "iPhone 15", "price": 5999 }
{ "index": { "_id": 2 }}
{ "name": "MacBook Pro", "price": 12999 }

# 调整刷新间隔
PUT /products/_settings
{
  "index": {
    "refresh_interval": "30s"
  }
}

查询优化

  • 使用 filter 代替 query:filter 不评分,性能更好
  • 限制返回字段:使用 _source 过滤
  • 使用 scroll 分页:深度分页使用 scroll
  • 使用 search_after:更高效的深度分页
# 使用 _source 过滤
GET /products/_search
{
  "_source": ["name", "price"],
  "query": {
    "match": {
      "name": "iPhone"
    }
  }
}

# 使用 scroll 分页
GET /products/_search?scroll=1m
{
  "size": 100,
  "query": {
    "match_all": {}
  }
}

# 使用 search_after
GET /products/_search
{
  "size": 10,
  "query": {
    "match_all": {}
  },
  "sort": [
    {"_id": "asc"}
  ],
  "search_after": ["last_id"]
}

硬件优化

  • 内存:建议 50% 给 JVM,50% 给系统缓存
  • CPU:多核 CPU 更好
  • 磁盘:使用 SSD,RAID 0 或 RAID 10
  • 网络:万兆网卡

最佳实践

  • 合理设计索引:根据查询需求设计索引结构
  • 使用别名:索引别名方便切换和管理
  • 定期清理旧数据:使用 ILM(索引生命周期管理)
  • 监控集群状态:使用 Kibana 或第三方工具监控
  • 备份策略:定期备份快照
  • 使用 Reindex:重建索引时使用 Reindex API

总结

ElasticSearch 是一个强大的分布式搜索引擎,提供了丰富的查询和聚合能力。本文介绍了 ElasticSearch 的核心概念、数据类型、Mapping、分词器、查询 DSL、聚合分析、集群架构以及性能优化技巧。掌握这些知识后,可以更好地应用 ElasticSearch 解决搜索和分析问题。