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 解决搜索和分析问题。