精通 Elasticsearch:最全中文教程
前言
在大数据时代,数据的存储、检索和分析变得前所未有的重要。Elasticsearch 作为一款开源、分布式、RESTful 风格的搜索和数据分析引擎,凭借其强大的实时搜索能力、灵活的架构和易用性,已成为日志分析、全文检索、业务数据分析等领域的首选技术。
本教程旨在为中文读者提供一个全面、深入的 Elasticsearch 学习路径,从基础概念到高级应用,助您彻底掌握 Elasticsearch 的精髓。
第一章:Elasticsearch 入门与核心概念
在深入学习之前,我们首先要理解 Elasticsearch 的核心组件和工作原理。
1.1 什么是 Elasticsearch?
Elasticsearch 是一个基于 Apache Lucene™ 的搜索引擎,它提供了一个分布式多用户能力的全文搜索引擎,具有 RESTful web 接口。Elasticsearch 是用 Java 开发的,它属于 Elastic Stack(前身为 ELK Stack)的核心部分,与 Logstash(数据收集)、Kibana(数据可视化)和 Beats(轻量级数据采集器)协同工作,构成一个强大的数据处理生态系统。
主要特点:
* 分布式:易于横向扩展,能够处理PB级数据。
* 实时性:近实时地存储、检索和分析数据。
* RESTful API:通过简单的 HTTP 请求即可进行交互。
* 全文搜索:支持强大的文本分析和搜索功能。
* 高可用性:通过数据副本机制确保数据安全。
* 架构灵活:支持多种数据类型和查询方式。
1.2 核心概念解析
理解这些概念是掌握 Elasticsearch 的基石:
- 索引(Index):类似于传统关系型数据库中的“数据库”。它是具有相似特征的文档集合。例如,您可以有一个名为
logs的索引用于存储日志数据,或一个名为products的索引用于存储商品信息。 - 文档(Document):Elasticsearch 的最小数据单元,是 JSON 格式的数据。它类似于关系型数据库中的“行”。每个文档在索引中都有一个唯一的 ID。
json
{
"title": "精通 Elasticsearch",
"author": "张三",
"publish_date": "2023-01-01",
"tags": ["Elasticsearch", "教程", "大数据"]
} - 字段(Field):文档中的键值对。例如,上面的文档中
title、author、publish_date、tags都是字段。 - 映射(Mapping):定义了索引中文档及其字段的类型、如何存储以及如何被索引的机制。它类似于关系型数据库中的“表结构定义”。例如,可以将
publish_date字段映射为date类型,title映射为text类型用于全文搜索,author映射为keyword类型用于精确匹配。 - 分片(Shard):Elasticsearch 将索引划分为多个分片。每个分片都是一个独立的 Lucene 索引。分片是数据分布和并行处理的基本单位,有助于横向扩展。
- 主分片(Primary Shard):负责存储索引的部分数据。一个文档只存在于一个主分片中。
- 副本分片(Replica Shard):主分片的副本。它们提供数据冗余以应对硬件故障,并能提高搜索吞吐量。
- 节点(Node):运行 Elasticsearch 实例的服务器。一个节点可以存储数据、参与集群索引和搜索功能。
- 主节点(Master Node):负责管理集群的元数据,如创建/删除索引、管理节点加入/离开集群。
- 数据节点(Data Node):负责存储索引数据并执行数据相关的操作,如 CRUD、搜索、聚合。
- 协调节点(Coordinating Node):接收客户端请求,将请求路由到正确的分片,并聚合结果。所有节点都可以充当协调节点。
- 预处理节点(Ingest Node):允许在索引文档之前执行各种转换。
- 集群(Cluster):一个或多个节点的集合,共同存储所有数据并提供索引和搜索能力。集群通过一个唯一的名称来标识。
第二章:安装与基本配置
本章将指导您如何在不同环境中安装 Elasticsearch 并进行基本配置。
2.1 安装 Elasticsearch
Elasticsearch 支持多种安装方式,这里以 Docker 和本地安装为例:
方式一:使用 Docker (推荐)
Docker 是部署 Elasticsearch 最便捷的方式,尤其适合开发和测试环境。
- 安装 Docker:确保您的系统已安装 Docker 和 Docker Compose。
-
创建
docker-compose.yml文件:
“`yaml
version: ‘3.8’
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.0 # 根据需要选择版本
container_name: elasticsearch
environment:
– discovery.type=single-node # 单节点模式
– “ES_JAVA_OPTS=-Xms512m -Xmx512m” # Java堆内存设置
ulimits:
memlock:
soft: -1
hard: -1
volumes:
– esdata:/usr/share/elasticsearch/data # 数据持久化
ports:
– 9200:9200
– 9300:9300
networks:
– es-netkibana:
image: docker.elastic.co/kibana/kibana:7.17.0 # 与Elasticsearch版本对应
container_name: kibana
environment:
– ELASTICSEARCH_HOSTS=http://elasticsearch:9200
ports:
– 5601:5601
networks:
– es-net
depends_on:
– elasticsearchvolumes:
esdata:
driver: localnetworks:
es-net:
driver: bridge
``docker-compose.yml
3. **启动容器**:在文件所在目录执行docker-compose up -d。http://localhost:9200
4. **验证**:访问和http://localhost:5601`,应该能看到 Elasticsearch 和 Kibana 正常运行。
方式二:本地安装 (Linux/macOS)
- 下载:从 Elastic 官网下载对应操作系统的 Elasticsearch 发行版(ZIP 或 TAR.GZ)。
- 解压:将下载的文件解压到您选择的目录。
- 配置 Java 环境:Elasticsearch 需要 Java 运行环境,确保您的系统已安装 JDK 8 或更高版本。
- 启动:进入 Elasticsearch 解压目录,运行
./bin/elasticsearch(Linux/macOS) 或.\bin\elasticsearch.bat(Windows)。 - 验证:访问
http://localhost:9200。
2.2 基本配置 config/elasticsearch.yml
cluster.name:集群名称,所有属于同一个集群的节点必须使用相同的集群名称。node.name:节点名称,每个节点应具有唯一的名称。path.data:数据存储路径。path.logs:日志存储路径。network.host:绑定 IP 地址,例如0.0.0.0或localhost。http.port:HTTP 端口,默认为9200。discovery.seed_hosts:集群中其他节点的 IP 地址列表,用于节点发现。cluster.initial_master_nodes:当集群首次启动时,指定有资格成为主节点的节点名称列表。
第三章:CRUD 基本操作
Elasticsearch 是一个 RESTful API 服务,所有的操作都通过 HTTP 请求(GET, POST, PUT, DELETE)来完成。
3.1 索引文档 (Index a Document)
将数据存储到 Elasticsearch 称为索引文档。
方式一:自动生成 ID (POST请求)
bash
POST /<index_name>/_doc
{
"field1": "value1",
"field2": "value2"
}
示例:
bash
POST /articles/_doc
{
"title": "Elasticsearch 学习指南",
"author": "李四",
"content": "这是一篇关于 Elasticsearch 的入门教程。",
"views": 100
}
响应会包含文档的 _id 和 _version。
方式二:指定 ID (PUT请求)
bash
PUT /<index_name>/_doc/<_id>
{
"field1": "value1",
"field2": "value2"
}
示例:
bash
PUT /articles/_doc/1
{
"title": "Elasticsearch 进阶技巧",
"author": "王五",
"content": "学习 Elasticsearch 的高级用法。",
"views": 200
}
3.2 获取文档 (Get a Document)
根据文档 ID 检索单个文档。
bash
GET /<index_name>/_doc/<_id>
示例:
bash
GET /articles/_doc/1
响应会返回文档的 _source 字段,其中包含原始文档数据。
3.3 更新文档 (Update a Document)
更新现有文档。Elasticsearch 内部会先删除旧文档,再索引新文档。
方式一:完全替换 (PUT请求)
通过 PUT /<index_name>/_doc/<_id> 重新提交整个文档。如果文档不存在,则创建;如果存在,则完全替换。
bash
PUT /articles/_doc/1
{
"title": "Elasticsearch 进阶技巧与实战",
"author": "王五",
"content": "深入学习 Elasticsearch 的高级用法和实战案例。",
"views": 250,
"last_updated": "2023-10-26"
}
方式二:部分更新 (POST请求配合 _update)
使用 _update API 结合 doc 或 script 字段进行局部更新,更高效。
bash
POST /<index_name>/_update/<_id>
{
"doc": {
"field_to_update": "new_value"
}
}
示例:
bash
POST /articles/_update/1
{
"doc": {
"views": 250,
"last_updated": "2023-10-26"
}
}
或者使用脚本:
bash
POST /articles/_update/1
{
"script": {
"source": "ctx._source.views += params.views_increment",
"lang": "painless",
"params": {
"views_increment": 50
}
}
}
3.4 删除文档 (Delete a Document)
根据文档 ID 删除单个文档。
bash
DELETE /<index_name>/_doc/<_id>
示例:
bash
DELETE /articles/_doc/1
3.5 批量操作 (Bulk API)
Bulk API 允许在单个请求中执行多个索引、更新、删除操作,大大提高效率。
bash
POST /_bulk
{ "index" : { "_index" : "articles", "_id" : "2" } }
{ "title" : "分布式系统原理", "author" : "赵六" }
{ "create" : { "_index" : "articles", "_id" : "3" } }
{ "title" : "Kibana 可视化", "author" : "钱七" }
{ "update" : { "_index" : "articles", "_id" : "2" } }
{ "doc" : {"views" : 50} }
{ "delete" : { "_index" : "articles", "_id" : "3" } }
第四章:映射 (Mapping)
映射定义了文档中的字段如何存储和索引。它是 Elasticsearch 能够高效搜索和聚合的关键。
4.1 动态映射 (Dynamic Mapping)
当您第一次索引一个文档时,如果该文档的字段之前未被定义映射,Elasticsearch 会尝试根据字段的值自动推断其数据类型并创建映射。
示例:
如果您索引一个包含 "age": 30 的文档,Elasticsearch 会自动将 age 字段映射为 long 类型。
动态映射虽然方便,但在生产环境中往往需要更精确的控制,以避免不必要的开销或不正确的类型推断。
4.2 显式映射 (Explicit Mapping)
通过 PUT _mapping API 手动定义或更新索引的映射。
创建索引时指定映射:
bash
PUT /my_blog
{
"mappings": {
"properties": {
"id": { "type": "keyword" },
"title": { "type": "text", "analyzer": "ik_smart" }, # 使用中文分词器
"content": { "type": "text", "analyzer": "ik_max_word" },
"tags": { "type": "keyword" },
"publish_date": { "type": "date", "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" },
"author": {
"type": "object",
"properties": {
"name": { "type": "keyword" },
"email": { "type": "keyword" }
}
},
"views": { "type": "integer" },
"is_published": { "type": "boolean" }
}
}
}
为现有索引添加新字段映射: (不能修改现有字段类型)
bash
PUT /my_blog/_mapping
{
"properties": {
"category": { "type": "keyword" }
}
}
4.3 常用数据类型
- 字符串类型:
text:用于全文搜索的文本字段,会进行分词处理。keyword:用于精确匹配、过滤和聚合的字符串字段,不会分词。
- 数值类型:
long,integer,short,byte,double,float,half_float,scaled_float。 - 日期类型:
date。支持多种日期格式。 - 布尔类型:
boolean。 - 二进制类型:
binary。 - 复合类型:
object:JSON 对象。nested:处理对象数组,确保数组中的每个对象独立索引。
- 地理位置类型:
geo_point,geo_shape。
重要提示:
* 一旦一个字段被映射,其类型就不能更改。如果需要更改类型,必须重建索引。
* 对于既需要全文搜索又需要精确匹配的字段,通常会使用 multi-fields,即为同一个字段定义 text 和 keyword 两种类型。
json
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256 # 忽略超过256个字符的keyword
}
}
}
第五章:搜索 (Searching)
搜索是 Elasticsearch 的核心功能。它使用 Query DSL (Domain Specific Language) 来构建复杂的查询。
5.1 Query DSL 基础
所有搜索请求都发送到 _search 端点,并通过请求体中的 JSON 来定义查询。
bash
GET /<index_name>/_search
{
"query": {
"match_all": {} # 查询所有文档
}
}
5.2 常用查询类型
match_all查询:匹配所有文档,通常用于调试或获取所有数据。
json
{ "query": { "match_all": {} } }match查询:最常用的全文搜索查询,它会根据字段的分析器对查询字符串进行分词。
json
{ "query": { "match": { "content": "Elasticsearch 教程" } } }term查询:用于精确匹配,查询字段中未经分析的词条(通常用于keyword类型字段)。
json
{ "query": { "term": { "tags": "大数据" } } }terms查询:与term类似,但可以匹配多个词条。
json
{ "query": { "terms": { "tags": ["大数据", "教程"] } } }range查询:查找某个范围内的值。
json
{ "query": { "range": { "views": { "gte": 100, "lte": 500 } } } }gt(大于),gte(大于等于)lt(小于),lte(小于等于)
exists和missing查询:检查文档是否包含某个字段或缺少某个字段。
json
{ "query": { "exists": { "field": "tags" } } }prefix查询:匹配以指定前缀开头的词条。
json
{ "query": { "prefix": { "author.name": "张" } } }wildcard查询:支持通配符*(零个或多个字符) 和?(单个字符)。
json
{ "query": { "wildcard": { "title": "Elasticsearch*" } } }
注意: 通配符查询性能较低,应谨慎使用。regexp查询:支持正则表达式匹配。
json
{ "query": { "regexp": { "author.email": ".*@example.com" } } }
注意: 正则表达式查询性能非常低,应谨慎使用。
5.3 复合查询 (Compound Queries)
通过布尔逻辑组合多个查询条件。
bool查询:must:文档必须匹配这些查询,并且会影响评分。filter:文档必须匹配这些查询,但不影响评分(常用于过滤条件)。should:文档应该匹配这些查询,影响评分。must_not:文档不能匹配这些查询,不影响评分。
json
GET /articles/_search
{
"query": {
"bool": {
"must": [
{ "match": { "title": "Elasticsearch" } }
],
"filter": [
{ "term": { "is_published": true } },
{ "range": { "views": { "gte": 50 } } }
],
"should": [
{ "match": { "author": "张三" } }
],
"must_not": [
{ "match": { "tags": "测试" } }
]
}
}
}
5.4 全文搜索进阶
match_phrase查询:匹配短语,要求查询词条的顺序和相对位置与文档中完全一致。
json
{ "query": { "match_phrase": { "content": "入门教程" } } }- 高亮显示 (Highlighting):在搜索结果中,将匹配到的关键词用 HTML 标签高亮显示。
json
GET /articles/_search
{
"query": { "match": { "content": "教程" } },
"highlight": {
"fields": {
"content": {}
},
"pre_tags": ["<em>"],
"post_tags": ["</em>"]
}
}
5.5 排序 (Sorting)
对搜索结果进行排序。
json
GET /articles/_search
{
"query": { "match_all": {} },
"sort": [
{ "publish_date": { "order": "desc" } },
{ "views": { "order": "desc" } }
]
}
5.6 分页 (Paging)
使用 from 和 size 参数进行分页。
* from:从第几个结果开始返回 (0-based)。
* size:返回结果的数量。
json
GET /articles/_search
{
"query": { "match_all": {} },
"from": 10,
"size": 5
}
注意: from + size 的值不宜过大,因为会消耗大量内存。对于深度分页,应考虑使用 scroll 或 search_after。
第六章:聚合 (Aggregations)
聚合是 Elasticsearch 的另一个强大功能,它允许您从数据中提取统计信息和洞察。类似于 SQL 中的 GROUP BY。
6.1 度量聚合 (Metric Aggregations)
对一组文档计算度量,如总和、平均值、最小值、最大值等。
sum(总和)avg(平均值)min(最小值)max(最大值)value_count(值计数)cardinality(基数,即不重复值的数量)
示例:计算所有文章的平均浏览量
bash
GET /articles/_search
{
"size": 0, # 不返回文档,只返回聚合结果
"aggs": {
"avg_views": {
"avg": {
"field": "views"
}
}
}
}
6.2 桶聚合 (Bucket Aggregations)
将文档放入不同的“桶”中,每个桶代表一个分类。
terms(词条桶):根据字段的唯一值创建桶。
bash
GET /articles/_search
{
"size": 0,
"aggs": {
"articles_by_author": {
"terms": {
"field": "author.name.keyword", # 对keyword类型字段进行聚合
"size": 10 # 返回前10个作者
},
"aggs": { # 嵌套聚合:计算每个作者的文章平均浏览量
"avg_views_per_author": {
"avg": {
"field": "views"
}
}
}
}
}
}range(范围桶):根据数值范围创建桶。
bash
GET /articles/_search
{
"size": 0,
"aggs": {
"views_ranges": {
"range": {
"field": "views",
"ranges": [
{ "to": 100, "key": "<100" },
{ "from": 100, "to": 500, "key": "100-500" },
{ "from": 500, "key": ">500" }
]
}
}
}
}date_histogram(日期直方图桶):根据日期字段按时间间隔创建桶。
bash
GET /articles/_search
{
"size": 0,
"aggs": {
"articles_over_time": {
"date_histogram": {
"field": "publish_date",
"fixed_interval": "month", # 按月聚合
"format": "yyyy-MM"
},
"aggs": {
"total_views": {
"sum": {
"field": "views"
}
}
}
}
}
}
6.3 管道聚合 (Pipeline Aggregations)
在其他聚合结果之上运行的聚合。
max_bucket(最大桶)min_bucket(最小桶)avg_bucket(平均桶)sum_bucket(总和桶)derivative(导数)moving_average(移动平均)
示例:计算每个作者文章平均浏览量中的最大值
这需要在 articles_by_author 聚合之后再加一个 max_bucket 聚合。
第七章:高级主题
7.1 文本分析 (Text Analysis)
文本分析是 Elasticsearch 如何处理和索引文本的核心。
- 分析器 (Analyzer):由字符过滤器、分词器和词元过滤器组成。
- 字符过滤器 (Char Filter):预处理原始字符串,如去除 HTML 标签或替换字符。
- 分词器 (Tokenizer):将字符串拆分成独立的词元(tokens)。例如,标准分词器按空格和标点符号分词。
- 词元过滤器 (Token Filter):处理词元,如转换为小写、去除停用词、添加同义词、词干提取等。
自定义分析器:
bash
PUT /my_custom_index
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"type": "custom",
"tokenizer": "ik_smart", # 使用ik分词器
"filter": ["lowercase", "stop_words_filter"]
}
},
"filter": {
"stop_words_filter": {
"type": "stop",
"stopwords": ["的", "是", "和"]
}
}
}
},
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "my_analyzer"
}
}
}
}
注意: 对于中文,需要安装像 ik-analyzer 这样的第三方分词插件。
7.2 相关性评分 (Relevance Scoring)
Elasticsearch 使用相关性算法来计算搜索结果的匹配度,并根据评分排序。
- TF-IDF (Term Frequency-Inverse Document Frequency):经典的算法,衡量一个词在文档中的重要性以及在整个索引中的稀有程度。
- BM25 (Okapi BM25):Elasticsearch 8.0+ 默认使用的算法,是 TF-IDF 的优化版本。
可以通过 _score 字段查看文档的相关性评分。explain API 可以帮助您理解评分的计算过程。
7.3 Ingest Node (预处理节点)
Ingest Node 可以在文档被索引之前,通过处理器(processors)对文档进行转换、丰富或规范化。例如:
* 从字段中提取信息。
* 添加新的字段。
* 删除或重命名字段。
* 将 IP 地址转换为地理位置信息。
示例:创建一个管道,将 full_name 字段拆分为 first_name 和 last_name。
“`bash
PUT _ingest/pipeline/split_name
{
“description”: “Splits a full name into first and last name”,
“processors”: [
{
“split”: {
“field”: “full_name”,
“separator”: “\s+”,
“target_field”: “name_parts”
}
},
{
“set”: {
“field”: “first_name”,
“value”: “{{name_parts.0}}”
}
},
{
“set”: {
“field”: “last_name”,
“value”: “{{name_parts.1}}”
}
},
{
“remove”: {
“field”: “name_parts”
}
}
]
}
使用管道索引文档
PUT /users/_doc/1?pipeline=split_name
{
“full_name”: “John Doe”
}
``first_name
索引后,文档将包含和last_name` 字段。
7.4 安全 (Security)
生产环境强烈建议开启安全功能。Elastic Stack 提供了 X-Pack 安全功能(部分免费,部分商业)。
- 用户与角色:定义用户及其权限(读、写、管理等)。
- 认证:如用户名/密码、LDAP、SAML 等。
- 授权:基于角色的访问控制 (RBAC)。
- 传输层安全 (TLS/SSL):加密节点间通信和客户端与集群通信。
第八章:最佳实践与优化
8.1 索引设计原则
- 小而精的索引:避免创建过大的索引,一个索引应该包含相似的数据。
- 时间序列数据:使用日期后缀的索引名(例如
logs-2023-10),便于管理和删除旧数据。 - 合理设置分片数:
- 主分片数一旦创建就不能修改。副本分片可以随时修改。
- 每个分片都需要消耗文件句柄、内存和 CPU。
- 通常建议每个分片大小在 10GB-50GB 之间。
- 分片数应是数据节点数的整数倍,以确保均匀分布。
- 字段类型选择:
text用于全文搜索。keyword用于精确匹配、排序、聚合。- 对于不需要搜索或聚合的字段,可以设置为
enabled: false或index: false以节省存储空间和提高性能。
8.2 性能优化
- 硬件配置:
- 内存:Elasticsearch 是内存密集型应用,Java 堆内存通常设置为物理内存的 50%,但不超过 30.5GB。
- CPU:多核 CPU 有利于并行处理。
- 磁盘:SSD 硬盘性能远优于 HDD,是关键。
- 批量操作:使用
_bulkAPI 进行批量读写。 - 合理使用分片和副本:
- 增加主分片可以提高并行处理能力,但过多会增加管理开销。
- 增加副本分片可以提高搜索吞吐量和高可用性。
- JVM 调优:调整 Java 虚拟机参数。
- 缓存:合理利用 Elasticsearch 内部的各种缓存。
- 查询优化:
- 优先使用
filter上下文的bool查询,因为它不计算评分,速度更快。 - 避免
wildcard、regexp等低性能查询。 - 使用
scrollAPI 或search_after进行深度分页,而不是from/size。
- 优先使用
- 刷新间隔:对于写入密集型场景,可以适当增加
refresh_interval,减少刷新频率,但会增加数据可见的延迟。
8.3 数据备份与恢复
- 快照 (Snapshot):将索引或整个集群的数据备份到共享文件系统、云存储(如 S3)中。
- 恢复 (Restore):从快照中恢复数据。
示例:创建快照仓库
bash
PUT /_snapshot/my_backup_repo
{
"type": "fs",
"settings": {
"location": "/path/to/my_backup_location" # 需要在所有主节点和数据节点上配置
}
}
创建快照
bash
PUT /_snapshot/my_backup_repo/snapshot_1?wait_for_completion=true
恢复快照
bash
POST /_snapshot/my_backup_repo/snapshot_1/_restore
{
"indices": "my_index*",
"rename_pattern": "(.+)",
"rename_replacement": "restored_$1"
}
总结
本教程从 Elasticsearch 的基础概念出发,逐步深入到 CRUD 操作、映射、搜索、聚合以及高级主题和最佳实践。Elasticsearch 是一个功能强大且复杂的系统,精通它需要大量的实践和持续学习。
下一步学习建议:
* 阅读官方文档:Elasticsearch 的官方文档是最好的学习资源。
* Kibana 实战:熟练使用 Kibana 进行数据探索、可视化和管理。
* Elastic Stack 其他组件:学习 Logstash、Beats 如何与 Elasticsearch 协同工作。
* 集群管理与监控:深入了解如何部署、管理和监控生产环境中的 Elasticsearch 集群。
* 性能调优与故障排除:通过实际案例学习如何诊断和解决性能问题。
希望这份“精通 Elasticsearch:最全中文教程”能为您打开探索 Elasticsearch 世界的大门,祝您学习愉快!