
简介这是一份面向Java开发者与技术分享场景的ElasticSearch演示文稿共40页PPT从基础概念到最佳实践层层递进。内容先梳理ElasticSearch的前世今生解释其为什么能超越传统关系型数据库的精确匹配实现更智能的相关性搜索再结合Lucene到ElasticSearch的发展脉络说明分布式架构在可扩展性与实时性上的优势。随后详细介绍节点、集群、分片、副本等核心概念并围绕CRUD操作与最佳实践给出可直接借鉴的要点帮助读者在较短时间内建立完整的ES知识框架也方便团队内部进行技术分享或培训使用。资源包共1个文件为PPTX演示文稿压缩包大小约9.1MB整体内容紧凑、结构清晰。目前已有1040人学习下载对于希望系统了解ElasticSearch并快速准备分享材料或整理学习笔记的读者而言这份资料覆盖了从原理到操作的常见疑问是一份实用性较强的入门与分享参考。1. 从一条 SQL 说起为什么数据库搞不定「搜索」先抛一个反直觉的结论where name like %莎士比亚%不是搜索只是精确匹配的变种。它没有理解能力不知道「莎翁」和「Shakespeare」和「哈姆雷特」和「威尼斯的商人」之间的关系也不知道用户输成「沙土比亚」时想找的其实是「莎士比亚」。数据库的定位是存储搜索只是它顺手提供的附加能力而 ElasticSearch 的定位恰好相反它首先是搜索引擎为了能搜才去存数据——这个出发点决定了它在相关性匹配、分词、倒排索引、分布式扩容上的投入远非数据库可比。这篇内容基于一份技术分享 PPT 展开适合正在做 Java 后端、想搞清「ES 到底是什么、分片副本怎么配、和 Solr 比怎么选」的开发者读完能直接对照操作。2. 从 Lucene 到分布式ES 核心原理与节点、分片、副本的协作机制2.1 Lucene 的贡献与局限Lucene 是 Doug Cutting 在 1999 年用 Java 写出来的搜索引擎库2005 年成为 Apache 顶级项目。它提供了倒排索引、分词器、相关性评分等全套检索能力ES 底层就是建立在 Lucene 之上的。但你直接拿 Lucene 做工程会非常痛苦接口学习曲线陡峭只能嵌入 Java 代码里调用数据量大了之后原生不支持水平扩展所有索引拆分、节点协调都得自己实现。Compass 是 Shay Banon 在 2004 年基于 Lucene 做的一次封装尝试到了 2010 年重写并命名为 Elasticsearch核心变化就是引入了分布式能力同时把操作方式改成了 RESTful API让任何语言都能通过 HTTP 调用不再被绑死在 Java 生态里。理解 ES 的架构要从三个层面去看最底层是 Lucene 的索引文件与段segment管理中间层是 ES 的分片shard机制最上层是节点node与集群cluster的协调。Lucene 解决的是单机索引问题ES 解决的是多机协调问题分片则是两者之间的桥梁——每个分片本质上就是一个独立的 Lucene 索引。2.2 节点与集群一个进程就是一台「服务器」节点是 ES 实例底层就是一个 Java 进程。你在服务器上执行bin/elasticsearch启动一个进程就拥有一个节点在同一台机器上再启一个进程就是第二个节点。多个节点协同工作就组成集群。集群的价值体现在两件事一是数据可以跨节点分布单机存不下的数据可以水平扩展二是高可用只要每个分片至少有一个副本任何一个节点宕机数据仍然完整可查。这里有一个容易混淆的点节点和服务器不是一一对应的一台物理机上可以有多个节点。生产环境不建议这么做因为会互相争抢 CPU 和内存资源但开发环境或者你想快速搭一个三节点集群做验证一个机器上起三个 ES 进程是常见的做法。2.3 分片shard与副本replica数据如何被切散和冗余分片是 ES 分布式存储的最小单位。ES 把一个索引的数据切散到多个分片上每个分片都是一个完整的 Lucene 索引拥有独立的倒排结构和段文件。假设你创建一个索引配置 3 个主分片那这 3 个分片会分散在集群的不同节点上。用户读写数据时ES 通过文档 ID 的哈希值计算路由决定该文档落到哪个分片。# 创建索引时指定分片数量创建后主分片数不可修改 curl -X PUT localhost:9200/course -H Content-Type: application/json -d { settings: { number_of_shards: 3, number_of_replicas: 1 } }主分片数量一旦设定后续无法通过 API 直接修改。这是 ES 设计上的硬限制原因是文档到分片的路由算法依赖主分片数取模routing hash(document_id) % number_of_primary_shards。如果允许修改主分片数所有已有文档的归属关系会全部失效原分片上的数据无法正确归位。所以创建索引之前要想清楚数据量级后面如果确实需要扩主分片只能重建索引走 reindex 流程成本比较高。副本分片数量则没有这个限制可以随时动态调整。# 动态修改副本数量 curl -X PUT localhost:9200/course/_settings -H Content-Type: application/json -d { number_of_replicas: 2 }副本数量可以随时调大调小默认值是 1。副本的作用一是容灾主分片所在节点宕机后副本分片会提升为主分片二是分摊搜索压力查询请求可以同时打到主分片和副本分片上相当于给查询做了负载均衡。注意副本不会分担写入压力写入请求只会转发到主分片由主分片同步给副本。2.4 数据写入的流转Coordinating 节点与分片路由一次写入请求的完整链路是这样的客户端把请求发送到集群的任意节点这个节点充当协调节点coordinating node它根据文档 ID 计算路由把请求转发给目标主分片所在的节点主分片写入 Lucene 后同步给副本分片副本分片确认后协调节点返回成功响应。整个过程对客户端是透明的你不需要知道数据在哪个节点上。# 写入一条文档ES 自动执行路由 curl -X POST localhost:9200/course/_doc/1 -H Content-Type: application/json -d { title: 莎士比亚戏剧精读, teacher: 王老师, content: 本课程深度解析哈姆雷特、罗密欧与朱丽叶、威尼斯商人等经典作品 }路由计算用的是文档 ID 的哈希值对主分片数取模所以_doc/1这个 ID 决定了它必然落在固定的主分片上。如果写入时不指定 IDES 会生成一个随机 ID 再做路由。这个机制决定了批量写入时如果想让相关文档落在一起可以用 routing 参数手动指定路由键ES 会优先按 routing 计算而不是按文档 ID这在按用户、按租户隔离数据的场景下非常有用。3. CRUD 实战用 REST API 操作索引与文档Java 项目接入方案3.1 索引操作创建、查看、删除ES 的索引相当于关系型数据库中的「数据库 表」的复合概念一个索引对应一个数据集合。操作索引最直接的方式是使用 REST API任何语言都能通过 HTTP 调用。创建一个带 mapping 的索引可以在创建时指定字段类型和分词器# 创建索引并指定字段类型 curl -X PUT localhost:9200/course -H Content-Type: application/json -d { settings: { number_of_shards: 3, number_of_replicas: 1 }, mappings: { properties: { title: { type: text, analyzer: ik_max_word }, teacher: { type: keyword }, price: { type: float }, publish_date: { type: date } } } }text类型用于全文检索字段keyword类型用于精确匹配字段比如教师姓名、订单状态这类不需要分词的场景。analyzer指定分词器ik_max_word是中文分词插件ES 默认的 standard 分词器对中文支持很差会把整句话按字或按单个词切分搜索体验不好生产环境处理中文内容几乎必装 IK 分词器。查看索引信息可以使用GET /course/_mapping查看字段映射GET /_cat/indices?v查看集群中所有索引的健康状态和分片分布。删除索引用DELETE /course这个操作是不可恢复的生产环境要十分谨慎通常配合索引命名规范如course-2024.01和生命周期管理来做。3.2 文档的增删改ES 中一个文档就是一个 JSON 对象。新增文档用POST /course/_doc/1注意这里_doc是类型标识ES 7.x 之后类型概念已经被移除但_doc作为固定端点名保留了下来。指定 ID 的写入是幂等的同 ID 重复写入会覆盖原文档实际是删除旧文档再写入新文档版本号会递增。# 新增文档 curl -X POST localhost:9200/course/_doc/1 -H Content-Type: application/json -d { title: 莎士比亚戏剧精读, teacher: 王老师, price: 199.0, publish_date: 2024-03-01 } # 根据 ID 查询文档 curl -X GET localhost:9200/course/_doc/1 # 更新文档指定字段 curl -X POST localhost:9200/course/_update/1 -H Content-Type: application/json -d { doc: { price: 159.0 } } # 删除文档 curl -X DELETE localhost:9200/course/_doc/1_update走的是 partial update 语义只更新传入的字段其他字段保持原样不需要先查出来再整体覆盖。ES 的更新操作本质是「按需取文档 → 修改 → 重建索引 → 删除旧文档」所以更新频率过高的场景对写入性能会有影响这是底层 Lucene 不可变段的特性决定的ES 只能在这个框架下做优化。3.3 查询与 DSLES 和 SQL 思维的根本区别ES 的查询 DSLDomain Specific Language是 JSON 形式的查询语法核心逻辑和 SQL 对应关系如下SQL 概念ES DSL 概念示例where teacher 王老师term 查询{term: {teacher: 王老师}}where title like %莎士比亚%match 查询{match: {title: 莎士比亚}}order by price descsort{sort: [{price: desc}]}limit 0, 10from/size{from: 0, size: 10}count(*)聚合 count{size: 0, aggs: {...}}最关键的差异在term和match上。term是精确匹配查询词不会被分词直接和倒排索引中的词项做精确比对match是全文检索查询词先被分词器切分再分别去索引中匹配最后按相关性打分排序。# match 查询分词后匹配按相关性排序 curl -X GET localhost:9200/course/_search -H Content-Type: application/json -d { query: { match: { content: 莎士比亚 哈姆雷特 } }, highlight: { fields: { content: {} } } }highlight是 ES 很有代表性的搜索能力扩展它会把命中的关键词自动套上em标签方便前端做高亮展示这个功能用数据库 like 查询实现非常麻烦在 ES 里只是一个配置项。match_phrase则要求查询词按顺序连续出现适合搜索短语的场景。3.4 Java 客户端接入High Level REST Client 到 Java API ClientJava 项目接入 ES 有两条路线。旧路线是RestHighLevelClient在 ES 7.x 时代是最主流的客户端功能完整且文档丰富随着 8.x 的推出进入维护模式。新路线是 Elastic 官方推荐的Java API Client基于 Elasticsearch Java Client 库使用 builder 模式构造请求类型安全和 8.x 完全兼容。// Java API Client 连接 ES 并执行 match 查询 ElasticsearchClient client new ElasticsearchClient( new RestClientTransport( new RestClientBuilder( HttpHost.create(http://localhost:9200) ).build(), new JacksonJsonpMapper() ) ); SearchResponseCourse response client.search(s - s .index(course) .query(q - q .match(t - t .field(content) .query(莎士比亚) )), Course.class ); for (HitCourse hit : response.hits().hits()) { Course course hit.source(); System.out.println(course.getTitle()); }HttpHost.create指定 ES 服务地址集群环境可以传入多个地址实现负载均衡和故障转移。JacksonJsonpMapper负责 JSON 序列化是默认的映射器。hit.source()返回的是反序列化后的Course对象需要提前定义好对应的 POJO字段名和 ES mapping 中的字段名保持一致。实际项目中创建客户端建议使用单例模式因为客户端内部维护连接池频繁创建销毁会产生大量 TIME_WAIT 连接。4. 对比选型Solr vs ElasticSearch以及 ES 集群的写入优化与扩容策略4.1 Solr 与 ES 的差异这张对比表说清楚做技术选型时最常被问到的问题就是「ES 和 Solr 怎么选」。两者都基于 Lucene但从架构设计到使用体验差别很明显。对比维度ElasticSearchApache Solr分布式协调内置开箱即用依赖 ZooKeeper 外部协调数据类型仅支持 JSON支持 XML、JSON、CSV 等实时性写入后近实时可搜默认 refresh 1s新建索引时 IO 阻塞实时性弱学习曲线REST API 简单直接SolrConfig.xml 等配置文件较多社区活跃度Elastic 公司驱动社区活跃Apache 社区维护相对平稳云服务Elastic Cloud、阿里云、腾讯云等较少云厂商托管Solr 需要额外维护 ZooKeeper 集群部署复杂度明显更高ES 的分布式协调内置于各节点通过 zen discovery 机制自动发现节点并选举主节点。Solr 在持续大批量建索引时会出现 IO 阻塞查询响应变慢而 ES 通过分片和 Lucene 的段合并机制将写入影响控制得更好这也是 PPT 里强调 ES 实时性更优的原因。4.2 Elastic 生态三件套Beats、Logstash、Kibana 的定位ES 之所以不只是一个搜索引擎是因为 Elastic Stack 把数据采集、处理、存储、可视化串成了一条完整链路。Beats 是轻量级数据采集器以极低资源占用部署在业务服务器上采集日志和指标Logstash 负责数据的加工和清洗支持从 Beats 或数据库拉取数据后做过滤、格式转换再写入 ESKibana 是可视化层提供仪表盘、日志查看器和数据探索界面。典型日志分析架构是Filebeat → Logstash → Elasticsearch → Kibana其中 Logstash 是可选的数据简单时 Filebeat 可以直接写入 ES。这套生态对应到实际业务就是 PPT 里提到的客户场景日志管理、指标监控、APM 性能监控、网站搜索、舆情分析。日志场景关注的是数据量大时的写入吞吐和检索速度搜索场景关注的是相关性排序和分词质量两者对 ES 配置的侧重点完全不同——日志场景要把批量写入参数调大搜索场景要把 refresh 间隔调短。4.3 写入性能优化从 refresh_interval 到 bulk 批量提交ES 写入链路中有一个容易踩坑的概念是 refresh。文档写入 Lucene 后先进入内存 buffer默认 1 秒刷新一次生成新的 segment让新数据变得可搜索。每个 segment 是一个独立的 Lucene 索引文件过多的 segment 会导致查询变慢所以 ES 后台会做段合并。写入频繁的场景下默认配置会导致 segment 数量快速增长合并开销变大。# 批量写入bulk API curl -X POST localhost:9200/_bulk -H Content-Type: application/json -d { index: { _index: course, _id: 2 } } { title: 哈姆雷特精读, price: 129.0 } { index: { _index: course, _id: 3 } } { title: 麦克白解析, price: 149.0 } { index: { _index: course, _id: 4 } } { title: 仲夏夜之梦, price: 99.0 } _bulkAPI 是 ES 写入性能的关键原理是一次 HTTP 请求携带多条文档写入操作减少网络往返和解析开销。单条逐次写入的吞吐量通常在每秒几千条bulk 批量提交可以提升到每秒数万甚至数十万条取决于文档大小和集群配置。常见调优参数是index.refresh_interval日志写入场景把它调大到 30 秒甚至更高换取更低的合并开销和更高的写入吞吐面向用户的搜索场景保持默认的 1 秒即可保证数据近实时可见。4.4 集群扩容从 3 nodes 到水平扩展ES 的水平扩展逻辑是「不搬数据只搬分片」。集群从 3 节点扩容到 4 节点时ES 会自动把部分分片从原节点迁移到新节点重新实现负载均衡。主分片数量决定了数据分布的最大粒度副本数量决定了冗余能力。假设一个索引配了 3 个主分片加 1 个副本那就是 6 个分片分布在集群中3 个节点每节点两个分片扩容到 4 节点后ES 会自动迁移让 6 个分片尽量均匀分布在 4 个节点上。# 查看集群健康状态和各索引分片分布 curl -X GET localhost:9200/_cluster/health?pretty curl -X GET localhost:9200/_cat/shards?v_cluster/health返回的status字段有三个值green表示所有主分片和副本分片都已分配yellow表示主分片正常但副本未分配常见于单节点集群red表示存在未分配的主分片数据可能有丢失风险。看到red时优先检查节点状态和磁盘空间用_cat/shards可以定位具体是哪个分片异常。ES 的自动分片均衡是默认开启的不需要额外配置。5. Windows 开发环境启动技巧ES 7.17 配置优化与 License 边界结合当前网上的高频检索词这里收一个开发阶段最实用的场景Windows 上启动 ES 做本地验证。ES 官方支持 Windows 平台但直接解压启动经常会遇到两个问题一是 JDK 版本不匹配导致启动失败二是内存配置不合适导致运行卡顿。ES 7.17 内置了 JDK不需要额外安装 Java 环境但需要确认JAVA_HOME环境变量没有指向过旧或过新的 JDK 版本——ES 7.17 要求 JDK 11 到 17 之间如果你机器上装的是 JDK 8启动就会报Unsupported Java version错误这种情况直接删掉JAVA_HOME或临时修改启动脚本即可。内存配置是 Windows 本地开发最容易忽略的一环。ES 默认堆内存是 1GB如果你的机器有 16GB 内存完全可以调大来提升本地性能。修改config/jvm.options文件中的以下参数-Xms2g -Xmx2gXms和Xmx分别设定堆内存的初始值和最大值两者设为相同值可以避免 JVM 运行时动态扩容导致性能抖动。本地开发建议设置 2GB 即可不用贪多——ES 所在机器内存不足时会在日志里反复报unable to create native thread或频繁触发 GC调大堆内存往往能直接解决。Windows 下启动后验证是否成功访问http://localhost:9200/会返回包含集群名称和版本号的 JSON 信息。关于 License这里有一个很多人没注意到的边界ES 从 7.11 版本开始将核心的basiclicense 免费开放包括安全认证TLS都在免费范围内但从 8.0 起部分高级功能被划入 Elastic License 2.0 或 Enterprise 订阅某些功能如最新的 RRF 混合检索在企业版才可用。7.17.0 是 7.x 系列的最后一个版本也是许多人选择的免费稳定版本8.x 主要引入了_source默认关闭、内置安全认证等破坏性变更升级前要验证客户端兼容性。本地开发和学习用 7.17 足够等真正要上生产再评估是否需要付费功能更合理。最后分享一个分片数设定的经验。ES 7.x 之前默认 5 个主分片7.x 开始改为 1 个主分片。很多人从老文章里学到「分片越多性能越好」这个认知已经过时了——每个分片都有独立的 Lucene 段管理和查询开销分片过多会导致小分片碎片化查询反而变慢。单节点开发环境直接用默认 1 主分片加 1 副本即可生产环境按「单分片控制在 30GB-50GB 以内」估算分片数例如预计数据总量 150GB设 3-5 个主分片就足够了不要盲目追求分片数量ES 的性能瓶颈通常不在分片数量上而在查询复杂度和磁盘 IO 上。本文还有配套的精品资源点击获取