DukeDuke
主页
项目文档
技术文档
  • 单机版
  • 微服务
  • 代办项目
  • 优鲜项目
项目管理
关于我们
主页
项目文档
技术文档
  • 单机版
  • 微服务
  • 代办项目
  • 优鲜项目
项目管理
关于我们
  • 技术文档

    • 网络原理

      • 交换机
      • 路由器
      • TCP/IP协议
      • HTTP 与 HTTPS
    • 软件架构

      • 什么是软件架构
      • 分层架构
      • 微服务架构
      • 事件驱动架构
      • 领域驱动设计(DDD)
      • 架构图
      • 高并发系统
    • Vue3

      • Vue3简介
      • Vue3响应式系统
      • Vue3组合式API
      • Vue3生命周期
      • Vue3模板语法
      • Vue3组件系统
      • Vue3 路由系统
      • Vue3 状态管理
      • Vue3 性能优化
      • Vue3 TypeScript 支持
      • Vue3 项目实战
      • VUE 面试题大全
      • Node.js 安装
    • JAVA

      • JVM

        • 认识JVM
        • JVM类加载器
        • 运行时数据区
        • 执行引擎
        • 本地方法接口
        • 本地方法库
        • JVM垃圾回收
        • JVM性能监控
        • JVM调优
      • 设计模式
        • 单例模式
        • 工厂模式
        • 策略模式
        • 适配器模式
        • 建造者模式
        • 原型模式
        • 装饰器模式
        • 代理模式
        • 外观模式
        • 享元模式
        • 组合模式
        • 桥接模式
      • Java多线程

        • Java 线程基础详解
        • Java 线程池详解
        • Java ThreadLocal 详解
        • Java volatile 详解
        • Java 线程间通信详解
        • Java 线程安全详解
        • Java 线程调度详解
        • Java 线程优先级详解

        • Java 线程中断详解
        • Java 线程死锁详解
      • Java反射
      • Java 面试题

        • Java 基础概念面试题
        • Java 面向对象编程面试题
        • Java 集合框架面试题
        • Java 多线程与并发面试题
        • JVM 与内存管理面试题
        • Java I/O 与 NIO 面试题
        • Java 异常处理面试题
        • Java 反射与注解面试题
        • Java Spring 框架面试题
        • Java 数据库与 JDBC 面试题
        • Java 性能优化面试题
        • Java 实际项目经验面试题
        • Java 高级特性面试题
        • Java 面试准备建议
    • Python

      • Python简介
      • Python安装
      • Python hello world
      • Python基础语法
      • Python数据类型
      • Python数字
      • Python字符串
      • Python列表
      • Python元组
      • Python字典
      • Python日期时间
      • Python文件操作
      • Python异常处理
      • Python函数
      • Python类
      • Python模块
      • Python包
      • Python多线程
      • Python面向对象
      • Python爬虫
      • Django web框架
      • Python 面试题

        • Python 面试题导航
        • Python 基础概念
        • Python 面向对象编程
        • Python 数据结构
        • Python 高级特性
        • Python 框架
        • Python 性能优化
        • Python 项目经验
    • Spring

      • Spring
      • Springboot
      • Spring Security 安全框架
      • SpringBoot 中的事件详解
      • SpringBoot 中的定时任务详解
      • SpringBoot 自动装配原理与源码解释
    • Mybatis

      • Mybatis
      • Mybatis-Plus
    • 数据库

      • Redis

        • Redis简介
        • Redis(单机)安装
        • Redis配置
        • Redis数据结构
        • RDB、AOF 和混合持久化机制
        • Redis内存管理
        • Redis缓存一致性
        • Redis缓存穿透
        • Redis缓存击穿
        • Redis缓存雪崩
        • Redis Lua脚本
        • Redis主从复制
        • Redis哨兵模式
        • Redis集群
        • Redis数据分片
        • Redis CPU使用率过高
        • Redis面试题
      • MySQL

        • MySQL简介
        • MySQL安装
        • MySQL配置
        • MYSQL日常维护
        • MYSQL优化-慢查询
        • MYSQL优化-索引
        • MYSQL数据库设计规范
    • 消息队列

      • RocketMQ
      • Kafka
      • RabbitMQ
      • 消息队列面试题
    • 微服务

      • SpringCloud 微服务
      • Eureka 注册中心
      • Nacos 注册中心
      • Gateway 网关
      • Feign 服务调用
      • Sentinel 限流 与 熔断
      • Seata 分布式事务
      • CAP 理论
      • Redis 分布式锁
      • 高并发系统设计
    • ELK日志分析系统

      • Elasticsearch 搜索引擎
      • Logstash 数据处理
      • Kibana 可视化
      • ELK 实战
    • 开放API

      • 开放API设计
      • 开放API示例项目
    • 人工智能

      • 人工智能简介
      • 机器学习

      • 深度学习

      • 自然语言处理

      • 计算机视觉

        • CUDA与cuDNN详细安装
        • Conda 安装
        • Pytorch 深度学习框架
        • yolo 目标检测
        • TensorRT 深度学习推理优化引擎
        • TensorFlow 机器学习
        • CVAT 图像标注
        • Windows 下安装 CUDA、cuDNN、TensorRT、TensorRT-YOLO 环境
        • Windows10+CUDA+cuDNN+TensorRT+TensorRT-YOLO 部署高性能YOLO11推理
    • 大数据

      • 大数据简介
      • Hadoop 数据存储
      • Flume 数据采集
      • Sqoop 数据导入导出
      • Hive 数据仓库
      • Spark 数据处理
      • Flink 数据处理
      • Kafka 数据采集
      • HBase 数据存储
      • Elasticsearch 搜索引擎
    • 图像处理

      • 图像处理简介
      • 医学图像web呈现
      • 医学图像处理
      • 切片细胞分离问题
    • 服务器&运维

      • Linux 系统

        • Linux 系统管理
        • Linux 网络管理
        • Linux 文件管理
        • Linux 命令大全
      • Nginx Web 服务器

        • Nginx 安装 与 配置
        • Nginx 负载均衡
        • Nginx SSL证书配置
        • Nginx Keepalived 高可用
      • Docker 容器

        • Docker 简介
        • Docker 安装与配置
        • Docker 命令
        • Docker 部署 Nginx
        • Docker 部署 MySQL
        • Docker 部署 Redis
      • 服务器

        • 塔式服务器
        • 机架式服务器
        • 刀片服务器
      • Git 版本控制
      • Jenkins 持续集成
      • Jmeter 性能测试
      • Let's Encrypt 免费SSL证书
    • 简历

      • 项目经理简历
      • 开发工程师简历

Elasticsearch 技术文档

目录

  • 概述
  • 核心原理
  • 架构设计
  • 核心概念
  • 数据模型
  • 索引机制
  • 搜索原理
  • 集群管理
  • 性能优化
  • 最佳实践
  • 安装与使用

概述

Elasticsearch 是一个基于 Apache Lucene 的分布式搜索引擎,专为处理大规模数据而设计。它提供了强大的全文搜索、实时分析、数据聚合等功能,广泛应用于日志分析、监控、安全分析等场景。

主要特性

  • 分布式架构:支持水平扩展,自动分片和复制
  • 实时搜索:近实时的搜索和分析能力
  • RESTful API:简单易用的 HTTP 接口
  • 多租户支持:支持多索引和类型
  • Schema-free:支持动态映射和字段类型推断

核心原理

1. 倒排索引原理

Elasticsearch 的核心是倒排索引(Inverted Index),这是全文搜索的基础数据结构。

倒排索引工作流程说明:

上图展示了 Elasticsearch 倒排索引的完整构建过程。从原始文档开始,经过分词处理将文本分解为词项,然后构建倒排索引结构。倒排索引由两个核心部分组成:词项字典和倒排列表。词项字典存储所有唯一的词项,如"elasticsearch"、"搜索"、"引擎"等,而倒排列表则记录每个词项出现在哪些文档中,包括文档 ID、位置信息和频率信息。这种结构使得搜索引擎能够快速定位包含特定词项的文档,实现高效的全文搜索功能。

倒排索引工作流程:

  1. 文档预处理:对原始文档进行分词、去停用词、词干提取等处理
  2. 词项提取:从处理后的文本中提取词项(terms)
  3. 索引构建:为每个词项建立到文档 ID 的映射关系
  4. 位置记录:记录词项在文档中的位置信息,用于短语查询

2. 分布式架构原理

Elasticsearch 采用分布式架构,通过分片(Sharding)和复制(Replication)实现高可用和水平扩展。

分布式架构层次说明:

上图展示了 Elasticsearch 分布式架构的四个核心层次。集群层包含主节点和多个数据节点,主节点负责集群管理和协调,数据节点负责数据存储和处理。索引层定义了数据的逻辑容器,如 logs 和 metrics 索引。分片层将索引物理分割为多个分片,包括主分片和副本分片,主分片负责数据写入,副本分片提供数据冗余和读取负载均衡。数据层由 Lucene 段文件组成,每个段都是一个独立的倒排索引,通过段合并机制优化存储和查询性能。这种分层架构确保了系统的高可用性、可扩展性和数据一致性。

分布式特性说明:

  • 主节点(Master Node):负责集群管理、索引创建、节点加入/离开等操作
  • 数据节点(Data Node):存储数据、处理搜索请求
  • 主分片(Primary Shard):负责数据写入和搜索
  • 副本分片(Replica Shard):提供数据冗余和读取负载均衡

架构设计

1. 整体架构

整体架构层次说明:

上图展示了 Elasticsearch 的完整系统架构,采用分层设计模式。客户端层提供多种接入方式,包括 REST API 客户端、Transport 客户端以及其他编程语言的客户端库,为不同应用场景提供灵活的接入选择。协调层是系统的核心控制中心,负责请求路由、负载均衡和结果聚合,确保请求能够高效地分发到合适的节点。处理层包含查询解析、聚合处理和脚本执行等核心功能模块,将用户请求转换为可执行的搜索操作。存储层基于 Apache Lucene 构建,提供高性能的索引存储、事务日志保证数据一致性,以及多级缓存系统提升查询性能。网络层提供 HTTP REST 接口和 Transport 协议,支持集群内部节点间的高效通信。这种分层架构实现了关注点分离,提高了系统的可维护性和扩展性。

2. 节点角色设计

节点角色功能说明:

上图展示了 Elasticsearch 集群中四种不同类型的节点角色及其核心功能。专用主节点是集群的大脑,负责集群管理、索引管理和节点发现等关键操作,确保集群的一致性和稳定性。数据节点是集群的存储核心,承担数据存储、搜索处理和聚合计算等重负载任务,直接与 Lucene 索引交互。协调节点作为请求的入口点,负责请求路由、结果聚合和负载均衡,将客户端请求分发到合适的数据节点。摄取节点专门处理数据预处理工作,包括数据清洗、格式转换和管道处理,为数据入库前的质量保证提供支持。这种角色分离设计使得每个节点都能专注于特定功能,提高了系统的整体性能和可维护性。

核心概念

1. 索引(Index)

索引是 Elasticsearch 中数据的逻辑容器,类似于关系数据库中的表。

索引结构说明:

上图展示了 Elasticsearch 索引的完整结构层次。索引是数据的逻辑容器,类似于关系数据库中的表,但具有更强大的分布式特性。每个索引包含多个主分片和副本分片,主分片负责数据写入和搜索操作,副本分片提供数据冗余和读取负载均衡。在 Elasticsearch 7.0 之后,类型(Type)概念已被废弃,索引直接包含文档。每个分片内部由多个段(Segment)组成,段是 Lucene 的基本存储单元,包含倒排索引数据。这种设计使得索引能够水平扩展,支持大规模数据处理,同时通过副本机制确保数据的高可用性。

索引特性:

  • 分片数量:决定并行处理能力
  • 副本数量:影响可用性和读取性能
  • 映射设置:定义字段类型和分析器
  • 设置配置:包括刷新间隔、合并策略等

2. 文档(Document)

文档是 Elasticsearch 中的基本数据单元,以 JSON 格式存储。

文档结构说明:

上图展示了 Elasticsearch 文档的内部结构和字段处理流程。文档是 Elasticsearch 中的基本数据单元,采用 JSON 格式存储,包含多个字段。每个字段都有特定的数据类型,如文本类型、关键词类型、日期类型等。文本类型字段会经过分析器处理,进行分词、去停用词等操作,适合全文搜索。关键词类型字段保持原始值,用于精确匹配和聚合操作。日期类型字段会进行标准化格式化,支持时间范围查询。这种字段类型设计使得 Elasticsearch 能够针对不同用途优化存储和查询性能,为复杂的搜索和分析需求提供灵活的数据模型支持。

3. 分片(Shard)

分片是索引的物理分割,每个分片都是一个独立的 Lucene 索引。

分片物理结构说明:

上图展示了 Elasticsearch 分片的物理结构和节点分布情况。分片是索引的物理分割单元,每个分片都是一个完整的 Lucene 索引,包含多个段(Segment)。主分片负责数据写入和搜索操作,而副本分片提供数据冗余和读取负载均衡。每个分片内部由多个段组成,段是 Lucene 的基本存储单元,包含倒排索引数据。分片分布在不同的节点上,主分片和其对应的副本分片不会分配在同一个节点上,确保数据的高可用性。这种设计使得 Elasticsearch 能够实现水平扩展,通过增加节点来提升集群的处理能力和存储容量。

数据模型

1. 字段类型

Elasticsearch 支持多种字段类型,每种类型都有特定的用途和优化。

字段类型分类说明:

上图展示了 Elasticsearch 支持的字段类型分类体系。核心类型包括 text、keyword、long、double、boolean、date 等基本数据类型,每种类型都有特定的用途。text 类型用于全文搜索,会进行分词处理;keyword 类型用于精确匹配和聚合;数值类型支持范围查询和数学运算。复杂类型包括 object、nested 和 array,用于处理结构化数据和数组。地理类型支持地理位置搜索,包括点坐标和形状数据。专用类型如 ip 地址、自动完成、词项计数等,为特定应用场景提供优化。这种丰富的字段类型体系使得 Elasticsearch 能够灵活处理各种数据格式,为不同的搜索和分析需求提供最佳性能。

2. 映射(Mapping)

映射定义了索引中文档的字段类型和属性。

映射结构说明:

上图展示了 Elasticsearch 映射的完整结构定义。映射是索引的元数据定义,决定了文档字段如何被索引和搜索。字段映射定义了具体的字段属性,包括字段名、字段类型、分析器和索引选项。字段名如 title、content、tags 等,字段类型如 text、keyword、date 等,分析器如 standard、english、whitespace 等。元数据映射包括_source(存储原始文档)、_all(全文搜索字段,已废弃)、_routing(路由字段)等系统字段。映射可以是显式定义的,也可以是动态推断的。合理的映射设计对于索引性能和搜索准确性至关重要,需要根据实际使用场景选择合适的字段类型和分析器。

索引机制

1. 写入流程

数据写入流程说明:

上图展示了 Elasticsearch 数据写入的完整时序流程。客户端首先向协调节点发送写入请求,协调节点根据路由规则将请求转发到对应的主分片。主分片接收到请求后,首先将操作写入事务日志(Translog),确保数据持久性,然后将文档添加到内存缓冲区。同时,主分片将数据同步到副本分片,副本分片也会写入自己的事务日志。只有当主分片和所有副本分片都确认写入成功后,协调节点才会向客户端返回成功响应。在后台,系统会定期将内存缓冲区中的数据刷新到磁盘,形成新的段文件,并通过段合并机制优化存储结构。这种设计确保了数据的一致性和持久性,同时提供了良好的写入性能。

2. 段合并机制

段合并过程说明:

上图展示了 Elasticsearch 段合并机制的完整过程。当文档写入时,首先存储在内存缓冲区中,定期刷新到磁盘形成新的段文件。随着时间推移,索引中会积累大量的小段文件,这会影响查询性能。段合并策略会识别相似大小的段,将它们合并成更大的段。在合并过程中,系统会删除重复的文档,优化存储空间,同时重建倒排索引结构。合并后的段文件数量减少,查询时需要搜索的段数量也相应减少,从而提升查询性能。段合并是一个后台异步过程,不会影响正常的写入操作,但会消耗一定的 CPU 和 I/O 资源,因此需要合理配置合并策略以平衡性能和资源消耗。

段合并策略:

  • Tiered 合并:按层合并,小段先合并
  • Log Byte Size 合并:基于段大小进行合并
  • 合并频率:通过merge.policy控制

搜索原理

1. 搜索流程

搜索处理流程说明:

上图展示了 Elasticsearch 搜索请求的完整处理流程。当客户端发送搜索请求时,首先经过查询解析阶段,将 JSON 查询转换为内部查询对象。然后进行查询重写,优化查询结构,如展开通配符、重写布尔查询等。接下来进行分片路由,确定需要搜索哪些分片,并将请求分发到相应的分片。每个分片在本地执行搜索操作,包括倒排索引查找、相关性评分计算等。所有分片的搜索结果被收集到协调节点,进行结果合并、排序和分页处理,最终返回给客户端。这种分布式搜索架构使得 Elasticsearch 能够并行处理大规模数据,提供快速的搜索响应。

2. 查询类型

查询类型分类说明:

上图展示了 Elasticsearch 支持的查询类型分类体系。全文查询包括 match、match_phrase、multi_match、query_string 等,这些查询会对文本进行分词处理,支持模糊匹配和相关性评分。词项查询包括 term、terms、range、exists 等,用于精确匹配和范围查询,不会进行分词处理。复合查询包括 bool、constant_score、dis_max 等,用于组合多个查询条件,实现复杂的搜索逻辑。特殊查询包括 geo_shape(地理形状查询)、more_like_this(相似文档查询)、script(脚本查询)等,为特定应用场景提供专门的查询能力。不同类型的查询有不同的性能特征和适用场景,合理选择查询类型对于提升搜索性能和准确性至关重要。

3. 相关性评分

相关性评分机制说明:

上图展示了 Elasticsearch 相关性评分的计算机制。传统的 TF-IDF 评分算法基于三个核心因素:词频(TF)、逆文档频率(IDF)和字段长度归一化。词频衡量词项在文档中的出现频率,频率越高分数越高。逆文档频率衡量词项在整个索引中的稀有程度,稀有词项获得更高分数。字段长度归一化确保短字段不会因为长度优势获得不公平的高分。Elasticsearch 5.0 之后采用更先进的 BM25 算法,在 TF-IDF 基础上增加了文档长度归一化和词频饱和处理,提供更准确的相关性评分。这种评分机制确保了搜索结果的质量,将最相关的文档排在前面,为用户提供更好的搜索体验。

集群管理

1. 集群状态

集群健康状态说明:

上图展示了 Elasticsearch 集群的三种健康状态及其含义。绿色状态表示集群完全健康,所有主分片和副本分片都可用,数据完整且可正常读写。黄色状态表示集群处于警告状态,所有主分片可用但部分副本分片不可用,这通常发生在节点故障或网络问题时,虽然不影响数据写入,但可能影响读取性能和可用性。红色状态表示集群处于错误状态,部分主分片不可用,这意味着数据丢失或无法访问,需要立即处理。集群状态的监控对于生产环境的稳定性至关重要,管理员需要根据状态及时采取相应的恢复措施,确保服务的连续性和数据的完整性。

2. 节点发现

节点发现过程说明:

上图展示了 Elasticsearch 集群节点发现的完整时序过程。当新节点启动时,会向集群中的其他节点发送 ping 请求,其他节点会返回 pong 响应确认连接。通过这种 ping-pong 机制,节点之间建立网络连接并交换集群信息。接下来进行主节点选举,通常选择节点 ID 最小的节点作为主节点,主节点负责集群管理和元数据维护。选举完成后,主节点向其他节点发送集群状态信息,包括节点列表、索引配置、分片分配等。其他节点确认主节点身份后,集群正式形成,开始正常的数据读写操作。这种自动发现机制使得 Elasticsearch 集群能够动态扩展,新节点可以自动加入集群,故障节点可以自动从集群中移除。

3. 分片分配

分片分配策略说明:

上图展示了 Elasticsearch 分片分配的三种主要策略。平衡策略包括节点间分片数量平衡、磁盘使用量平衡和 CPU 使用量平衡,确保集群资源得到均匀利用,避免某些节点过载。分配感知策略包括机架感知、数据中心感知和可用区感知,用于在分布式环境中确保数据的高可用性,避免将主分片和副本分片分配在同一个故障域内。过滤策略允许管理员通过标签或规则排除或包含特定节点,实现更精细的分片控制。这些策略可以组合使用,通过自动分配实现负载均衡,通过手动分配实现特定的部署要求。合理的分片分配策略对于集群的性能、可用性和资源利用率至关重要。

性能优化

1. 索引优化

索引优化策略说明:

上图展示了 Elasticsearch 索引优化的三个主要方面。映射优化包括合理选择字段类型、禁用不需要的字段和使用 keyword 而非 text 等策略,这些优化可以减少存储空间和提高查询性能。分片优化涉及合理设置分片数量、避免过度分片和考虑数据增长等因素,正确的分片配置对于集群的扩展性和性能至关重要。设置优化包括调整刷新间隔、配置合并策略和设置副本数量等参数,这些设置会影响写入性能、存储效率和可用性。索引优化是一个持续的过程,需要根据实际的数据特征、查询模式和硬件资源进行调优,以达到最佳的性能和资源利用率平衡。

2. 查询优化

查询优化策略说明:

上图展示了 Elasticsearch 查询优化的三个主要策略。查询结构优化包括使用 filter 而非 query、避免深度分页和合理使用聚合等技巧,这些优化可以显著提升查询性能。filter 查询不计算相关性评分,性能更好;深度分页会消耗大量资源,应避免使用;合理使用聚合可以减少数据传输量。索引使用优化包括使用路由、避免通配符查询和使用短语查询等策略,这些优化可以减少搜索范围,提高查询效率。缓存优化涉及查询缓存、字段数据缓存和分片请求缓存等,合理利用缓存可以大幅提升重复查询的性能。查询优化需要结合具体的业务场景和数据特征,通过监控和分析查询性能,持续优化查询策略。

3. 硬件优化

硬件优化策略说明:

上图展示了 Elasticsearch 硬件优化的四个主要方面。CPU 优化包括使用多核处理器和合理设置线程池,Elasticsearch 能够充分利用多核 CPU 的并行处理能力,通过调整线程池大小可以优化 CPU 资源利用。内存优化涉及配置足够的内存、避免交换和合理设置堆大小,Elasticsearch 对内存要求较高,需要为 JVM 堆分配足够的内存,同时避免使用交换分区影响性能。存储优化包括使用 SSD 存储、配置 RAID 和分离数据与日志,SSD 能够显著提升 I/O 性能,合理的 RAID 配置可以提供数据冗余和性能提升。网络优化包括使用高速网络和网络隔离,良好的网络环境对于集群间通信和客户端访问至关重要。硬件优化需要根据实际的数据规模、查询负载和预算进行综合考虑,选择最适合的硬件配置。

最佳实践

1. 索引设计原则

索引设计原则说明:

上图展示了 Elasticsearch 索引设计的四个核心原则。单一职责原则要求每个索引专注于特定用途,避免大索引,按时间分割数据,这样可以提高查询效率和管理便利性。合理分片原则建议分片数等于节点数乘以 1.5,避免过度分片,同时考虑数据增长因素,确保集群的扩展性。生命周期管理原则将数据分为热数据、温数据和冷数据,针对不同阶段的数据采用不同的存储策略和优化措施。映射优化原则强调明确字段类型、禁用不需要的字段和使用 keyword 优化等策略,这些优化可以显著提升索引性能和存储效率。遵循这些设计原则可以构建高性能、易维护的 Elasticsearch 集群,为业务提供稳定可靠的搜索服务。

3. 安全配置

安全配置体系说明:

上图展示了 Elasticsearch 安全配置的四个核心方面。认证机制包括用户名密码、LDAP 集成和 SAML 认证等多种方式,确保只有授权用户能够访问集群。授权系统通过角色定义、权限分配和资源访问控制,实现细粒度的权限管理,不同用户可以根据其职责获得相应的访问权限。加密保护包括传输层加密 TLS、数据加密和密钥管理,确保数据在传输和存储过程中的安全性。审计功能包括操作日志、访问记录和安全事件监控,帮助管理员追踪用户活动,及时发现安全威胁。完善的安全配置对于保护敏感数据和满足合规要求至关重要,需要根据组织的安全策略和法规要求进行合理配置。

安装与使用

1. 环境准备

系统要求

系统要求说明:

上图展示了 Elasticsearch 安装的系统要求。操作系统支持 Linux、Windows 和 macOS,需要 64 位系统,Linux 内核版本不低于 2.6。Java 环境要求 JDK 11 或更高版本,推荐使用 OpenJDK,需要正确设置 JAVA_HOME 环境变量。硬件配置建议内存至少 4GB,CPU 2 核以上,存储推荐使用 SSD 以获得更好的 I/O 性能。网络配置需要开放 9200 端口(HTTP API)和 9300 端口(节点间通信),并配置防火墙规则。这些要求确保了 Elasticsearch 能够稳定运行并发挥最佳性能。

安装步骤

1. 下载 Elasticsearch

# 下载最新版本
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.11.0-linux-x86_64.tar.gz

# 解压
tar -xzf elasticsearch-8.11.0-linux-x86_64.tar.gz
cd elasticsearch-8.11.0

2. 配置 Elasticsearch

# config/elasticsearch.yml
cluster.name: my-elasticsearch
node.name: node-1
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
network.host: 0.0.0.0
http.port: 9200
discovery.seed_hosts: ["localhost"]
cluster.initial_master_nodes: ["node-1"]

# 安全配置
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true

3. 启动 Elasticsearch

# 启动服务
./bin/elasticsearch

# 后台运行
./bin/elasticsearch -d

# 检查状态
curl -X GET "localhost:9200/_cluster/health?pretty"

2. Spring Boot 集成

项目依赖配置

<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Elasticsearch Spring Data -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>

    <!-- JSON 处理 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>

    <!-- 测试依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

应用配置

# application.yml
spring:
  elasticsearch:
    uris: http://localhost:9200
    connection-timeout: 1s
    socket-timeout: 30s

  # 日志配置
logging:
  level:
    org.springframework.data.elasticsearch: DEBUG
    org.elasticsearch: DEBUG

# 自定义配置
elasticsearch:
  index:
    name: products
    settings:
      number_of_shards: 3
      number_of_replicas: 1

实体类定义

// Product.java
package com.example.elasticsearch.entity;

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

@Document(indexName = "products")
public class Product {

    @Id
    private String id;

    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String name;

    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String description;

    @Field(type = FieldType.Keyword)
    private String category;

    @Field(type = FieldType.Double)
    private BigDecimal price;

    @Field(type = FieldType.Keyword)
    private List<String> tags;

    @Field(type = FieldType.Date)
    private LocalDateTime createTime;

    @Field(type = FieldType.Boolean)
    private Boolean active;

    // 构造函数
    public Product() {}

    public Product(String name, String description, String category,
                   BigDecimal price, List<String> tags) {
        this.name = name;
        this.description = description;
        this.category = category;
        this.price = price;
        this.tags = tags;
        this.createTime = LocalDateTime.now();
        this.active = true;
    }

    // Getter 和 Setter 方法
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public String getDescription() { return description; }
    public void setDescription(String description) { this.description = description; }

    public String getCategory() { return category; }
    public void setCategory(String category) { this.category = category; }

    public BigDecimal getPrice() { return price; }
    public void setPrice(BigDecimal price) { this.price = price; }

    public List<String> getTags() { return tags; }
    public void setTags(List<String> tags) { this.tags = tags; }

    public LocalDateTime getCreateTime() { return createTime; }
    public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }

    public Boolean getActive() { return active; }
    public void setActive(Boolean active) { this.active = active; }

    @Override
    public String toString() {
        return "Product{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", category='" + category + '\'' +
                ", price=" + price +
                ", tags=" + tags +
                '}';
    }
}

Repository 接口

// ProductRepository.java
package com.example.elasticsearch.repository;

import com.example.elasticsearch.entity.Product;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.math.BigDecimal;
import java.util.List;

public interface ProductRepository extends ElasticsearchRepository<Product, String> {

    // 基本查询方法
    List<Product> findByName(String name);
    List<Product> findByCategory(String category);
    List<Product> findByPriceBetween(BigDecimal minPrice, BigDecimal maxPrice);
    List<Product> findByActiveTrue();

    // 复杂查询
    List<Product> findByNameContainingOrDescriptionContaining(String name, String description);
    List<Product> findByTagsIn(List<String> tags);

    // 分页查询
    Page<Product> findByCategory(String category, Pageable pageable);

    // 自定义查询
    @Query("{\"bool\": {\"must\": [{\"match\": {\"name\": \"?0\"}}, {\"range\": {\"price\": {\"gte\": \"?1\", \"lte\": \"?2\"}}}]}}")
    List<Product> findByNameAndPriceRange(String name, BigDecimal minPrice, BigDecimal maxPrice);

    @Query("{\"bool\": {\"should\": [{\"match\": {\"name\": \"?0\"}}, {\"match\": {\"description\": \"?0\"}}], \"filter\": [{\"term\": {\"active\": true}}]}}")
    List<Product> searchActiveProducts(String keyword);
}

Service 层

// ProductService.java
package com.example.elasticsearch.service;

import com.example.elasticsearch.entity.Product;
import com.example.elasticsearch.repository.ProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;

import static org.elasticsearch.index.query.QueryBuilders.*;

@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    @Autowired
    private ElasticsearchOperations elasticsearchOperations;

    // 保存产品
    public Product saveProduct(Product product) {
        return productRepository.save(product);
    }

    // 批量保存
    public Iterable<Product> saveAllProducts(List<Product> products) {
        return productRepository.saveAll(products);
    }

    // 根据ID查找
    public Optional<Product> findById(String id) {
        return productRepository.findById(id);
    }

    // 查找所有产品
    public Iterable<Product> findAllProducts() {
        return productRepository.findAll();
    }

    // 根据名称查找
    public List<Product> findByName(String name) {
        return productRepository.findByName(name);
    }

    // 根据分类查找
    public List<Product> findByCategory(String category) {
        return productRepository.findByCategory(category);
    }

    // 价格范围查询
    public List<Product> findByPriceRange(BigDecimal minPrice, BigDecimal maxPrice) {
        return productRepository.findByPriceBetween(minPrice, maxPrice);
    }

    // 分页查询
    public Page<Product> findByCategoryWithPagination(String category, int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        return productRepository.findByCategory(category, pageable);
    }

    // 全文搜索
    public List<Product> searchProducts(String keyword) {
        return productRepository.searchActiveProducts(keyword);
    }

    // 高级搜索
    public SearchHits<Product> advancedSearch(String keyword, String category,
                                            BigDecimal minPrice, BigDecimal maxPrice) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        // 构建查询条件
        if (keyword != null && !keyword.isEmpty()) {
            queryBuilder.withQuery(boolQuery()
                .should(matchQuery("name", keyword))
                .should(matchQuery("description", keyword))
                .minimumShouldMatch(1));
        }

        // 添加过滤条件
        if (category != null && !category.isEmpty()) {
            queryBuilder.withFilter(termQuery("category", category));
        }

        if (minPrice != null || maxPrice != null) {
            queryBuilder.withFilter(rangeQuery("price")
                .gte(minPrice != null ? minPrice : 0)
                .lte(maxPrice != null ? maxPrice : Double.MAX_VALUE));
        }

        // 只查询活跃产品
        queryBuilder.withFilter(termQuery("active", true));

        NativeSearchQuery searchQuery = queryBuilder.build();
        return elasticsearchOperations.search(searchQuery, Product.class);
    }

    // 聚合查询
    public List<String> getCategories() {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withAggregations(AggregationBuilders.terms("categories")
            .field("category"));

        NativeSearchQuery searchQuery = queryBuilder.build();
        SearchHits<Product> searchHits = elasticsearchOperations.search(searchQuery, Product.class);

        // 处理聚合结果
        // 这里简化处理,实际应用中需要解析聚合结果
        return List.of("电子产品", "服装", "食品", "图书");
    }

    // 删除产品
    public void deleteProduct(String id) {
        productRepository.deleteById(id);
    }

    // 更新产品
    public Product updateProduct(String id, Product product) {
        if (productRepository.existsById(id)) {
            product.setId(id);
            return productRepository.save(product);
        }
        throw new RuntimeException("Product not found with id: " + id);
    }
}

Controller 层

// ProductController.java
package com.example.elasticsearch.controller;

import com.example.elasticsearch.entity.Product;
import com.example.elasticsearch.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/api/products")
@CrossOrigin(origins = "*")
public class ProductController {

    @Autowired
    private ProductService productService;

    // 创建产品
    @PostMapping
    public ResponseEntity<Product> createProduct(@RequestBody Product product) {
        Product savedProduct = productService.saveProduct(product);
        return ResponseEntity.ok(savedProduct);
    }

    // 批量创建产品
    @PostMapping("/batch")
    public ResponseEntity<Iterable<Product>> createProducts(@RequestBody List<Product> products) {
        Iterable<Product> savedProducts = productService.saveAllProducts(products);
        return ResponseEntity.ok(savedProducts);
    }

    // 根据ID获取产品
    @GetMapping("/{id}")
    public ResponseEntity<Product> getProduct(@PathVariable String id) {
        Optional<Product> product = productService.findById(id);
        return product.map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    // 获取所有产品
    @GetMapping
    public ResponseEntity<Iterable<Product>> getAllProducts() {
        Iterable<Product> products = productService.findAllProducts();
        return ResponseEntity.ok(products);
    }

    // 根据名称搜索
    @GetMapping("/search/name")
    public ResponseEntity<List<Product>> searchByName(@RequestParam String name) {
        List<Product> products = productService.findByName(name);
        return ResponseEntity.ok(products);
    }

    // 根据分类搜索
    @GetMapping("/search/category")
    public ResponseEntity<List<Product>> searchByCategory(@RequestParam String category) {
        List<Product> products = productService.findByCategory(category);
        return ResponseEntity.ok(products);
    }

    // 价格范围搜索
    @GetMapping("/search/price")
    public ResponseEntity<List<Product>> searchByPriceRange(
            @RequestParam BigDecimal minPrice,
            @RequestParam BigDecimal maxPrice) {
        List<Product> products = productService.findByPriceRange(minPrice, maxPrice);
        return ResponseEntity.ok(products);
    }

    // 分页查询
    @GetMapping("/search/category/paged")
    public ResponseEntity<Page<Product>> searchByCategoryPaged(
            @RequestParam String category,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size) {
        Page<Product> products = productService.findByCategoryWithPagination(category, page, size);
        return ResponseEntity.ok(products);
    }

    // 全文搜索
    @GetMapping("/search")
    public ResponseEntity<List<Product>> searchProducts(@RequestParam String keyword) {
        List<Product> products = productService.searchProducts(keyword);
        return ResponseEntity.ok(products);
    }

    // 高级搜索
    @GetMapping("/search/advanced")
    public ResponseEntity<SearchHits<Product>> advancedSearch(
            @RequestParam(required = false) String keyword,
            @RequestParam(required = false) String category,
            @RequestParam(required = false) BigDecimal minPrice,
            @RequestParam(required = false) BigDecimal maxPrice) {
        SearchHits<Product> searchHits = productService.advancedSearch(keyword, category, minPrice, maxPrice);
        return ResponseEntity.ok(searchHits);
    }

    // 获取所有分类
    @GetMapping("/categories")
    public ResponseEntity<List<String>> getCategories() {
        List<String> categories = productService.getCategories();
        return ResponseEntity.ok(categories);
    }

    // 更新产品
    @PutMapping("/{id}")
    public ResponseEntity<Product> updateProduct(@PathVariable String id, @RequestBody Product product) {
        try {
            Product updatedProduct = productService.updateProduct(id, product);
            return ResponseEntity.ok(updatedProduct);
        } catch (RuntimeException e) {
            return ResponseEntity.notFound().build();
        }
    }

    // 删除产品
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteProduct(@PathVariable String id) {
        productService.deleteProduct(id);
        return ResponseEntity.ok().build();
    }
}

配置类

// ElasticsearchConfig.java
package com.example.elasticsearch.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;

@Configuration
@EnableElasticsearchRepositories(basePackages = "com.example.elasticsearch.repository")
public class ElasticsearchConfig extends ElasticsearchConfiguration {

    @Override
    public ClientConfiguration clientConfiguration() {
        return ClientConfiguration.builder()
                .connectedTo("localhost:9200")
                .withConnectTimeout(1000)
                .withSocketTimeout(30000)
                .build();
    }
}

3. 使用示例

数据初始化

// DataInitializer.java
package com.example.elasticsearch.init;

import com.example.elasticsearch.entity.Product;
import com.example.elasticsearch.repository.ProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;

@Component
public class DataInitializer implements CommandLineRunner {

    @Autowired
    private ProductRepository productRepository;

    @Override
    public void run(String... args) throws Exception {
        // 清空现有数据
        productRepository.deleteAll();

        // 创建示例产品
        List<Product> products = Arrays.asList(
            new Product("iPhone 15", "苹果最新智能手机", "电子产品",
                       new BigDecimal("6999"), Arrays.asList("手机", "苹果", "5G")),
            new Product("MacBook Pro", "专业级笔记本电脑", "电子产品",
                       new BigDecimal("12999"), Arrays.asList("笔记本", "苹果", "专业")),
            new Product("Nike运动鞋", "舒适透气的运动鞋", "服装",
                       new BigDecimal("599"), Arrays.asList("运动鞋", "Nike", "舒适")),
            new Product("Java编程思想", "经典Java编程书籍", "图书",
                       new BigDecimal("89"), Arrays.asList("Java", "编程", "经典")),
            new Product("星巴克咖啡", "香浓醇厚的咖啡", "食品",
                       new BigDecimal("35"), Arrays.asList("咖啡", "星巴克", "香浓"))
        );

        // 保存产品
        productRepository.saveAll(products);

        System.out.println("数据初始化完成,共创建 " + products.size() + " 个产品");
    }
}

测试用例

// ProductServiceTest.java
package com.example.elasticsearch.service;

import com.example.elasticsearch.entity.Product;
import com.example.elasticsearch.repository.ProductRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
public class ProductServiceTest {

    @Autowired
    private ProductService productService;

    @Autowired
    private ProductRepository productRepository;

    @Test
    public void testSaveAndFindProduct() {
        // 创建产品
        Product product = new Product("测试产品", "这是一个测试产品", "测试分类",
                                   new BigDecimal("100"), List.of("测试", "产品"));

        // 保存产品
        Product savedProduct = productService.saveProduct(product);
        assertNotNull(savedProduct.getId());

        // 查找产品
        Optional<Product> foundProduct = productService.findById(savedProduct.getId());
        assertTrue(foundProduct.isPresent());
        assertEquals("测试产品", foundProduct.get().getName());
    }

    @Test
    public void testSearchByName() {
        List<Product> products = productService.findByName("iPhone");
        assertFalse(products.isEmpty());
        assertTrue(products.stream().anyMatch(p -> p.getName().contains("iPhone")));
    }

    @Test
    public void testSearchByCategory() {
        List<Product> products = productService.findByCategory("电子产品");
        assertFalse(products.isEmpty());
        products.forEach(p -> assertEquals("电子产品", p.getCategory()));
    }

    @Test
    public void testPriceRangeSearch() {
        List<Product> products = productService.findByPriceRange(
            new BigDecimal("1000"), new BigDecimal("8000"));
        assertFalse(products.isEmpty());
        products.forEach(p -> {
            assertTrue(p.getPrice().compareTo(new BigDecimal("1000")) >= 0);
            assertTrue(p.getPrice().compareTo(new BigDecimal("8000")) <= 0);
        });
    }

    @Test
    public void testFullTextSearch() {
        List<Product> products = productService.searchProducts("苹果");
        assertFalse(products.isEmpty());
        // 验证搜索结果包含苹果相关产品
        assertTrue(products.stream().anyMatch(p ->
            p.getName().contains("iPhone") || p.getName().contains("MacBook")));
    }
}

4. 监控与运维

健康检查

// ElasticsearchHealthIndicator.java
package com.example.elasticsearch.health;

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.stereotype.Component;

@Component
public class ElasticsearchHealthIndicator implements HealthIndicator {

    private final ElasticsearchOperations elasticsearchOperations;

    public ElasticsearchHealthIndicator(ElasticsearchOperations elasticsearchOperations) {
        this.elasticsearchOperations = elasticsearchOperations;
    }

    @Override
    public Health health() {
        try {
            // 执行简单查询检查连接
            elasticsearchOperations.indexOps("products").exists();
            return Health.up()
                    .withDetail("message", "Elasticsearch is running")
                    .build();
        } catch (Exception e) {
            return Health.down()
                    .withDetail("error", e.getMessage())
                    .build();
        }
    }
}

性能监控

// ElasticsearchMetrics.java
package com.example.elasticsearch.metrics;

import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.stereotype.Component;

@Component
public class ElasticsearchMetrics {

    @Autowired
    private MeterRegistry meterRegistry;

    @Autowired
    private ElasticsearchOperations elasticsearchOperations;

    public void recordSearchTime(long startTime) {
        long duration = System.currentTimeMillis() - startTime;
        meterRegistry.timer("elasticsearch.search.duration").record(
            java.time.Duration.ofMillis(duration));
    }

    public void incrementSearchCount() {
        meterRegistry.counter("elasticsearch.search.count").increment();
    }

    public void recordIndexingTime(long startTime) {
        long duration = System.currentTimeMillis() - startTime;
        meterRegistry.timer("elasticsearch.indexing.duration").record(
            java.time.Duration.ofMillis(duration));
    }
}

5. 最佳实践总结

性能优化建议

安全配置建议

通过以上完整的安装配置和 Spring Boot 集成示例,您可以快速搭建一个功能完整的 Elasticsearch 应用。这个示例涵盖了从基础配置到高级功能的各个方面,为实际项目开发提供了良好的参考基础。

总结

Elasticsearch 是一个功能强大的分布式搜索引擎,其核心优势在于:

  1. 高性能:基于 Lucene 的倒排索引提供快速的全文搜索能力
  2. 高可用:分布式架构和副本机制确保系统可靠性
  3. 易扩展:水平扩展能力支持大规模数据处理
  4. 功能丰富:支持复杂的聚合分析、地理搜索等高级功能

通过深入理解其核心原理、合理设计索引结构、优化查询性能,可以充分发挥 Elasticsearch 的优势,为各种应用场景提供强大的搜索和分析能力。

关键要点

  • 倒排索引是全文搜索的核心,通过词项到文档的映射实现快速检索
  • 分布式架构通过分片和副本实现高可用和水平扩展
  • 段合并机制优化存储空间和查询性能
  • 相关性评分确保搜索结果的质量和准确性
  • 合理的索引设计是性能优化的基础
  • 监控和运维是生产环境稳定运行的重要保障

通过掌握这些核心概念和最佳实践,可以构建高性能、高可用的 Elasticsearch 集群,为业务提供强大的搜索和分析能力。

最近更新:: 2025/10/20 11:08
Contributors: Duke
Next
Logstash 数据处理