Elasticsearch:从入门到生产落地

张开发
2026/4/21 3:10:19 15 分钟阅读

分享文章

Elasticsearch:从入门到生产落地
一、什么是 Elasticsearch为什么我们需要它Elasticsearch简称 ES是一个开源的、分布式的、RESTful 风格的搜索引擎和数据分析引擎。它基于 Lucene 库构建提供了简单易用的 API隐藏了 Lucene 的复杂性。ES 的核心特点分布式自动分片、自动负载均衡、自动故障转移近实时数据写入后 1 秒内即可被搜索到全文检索支持复杂的全文搜索、模糊搜索、高亮显示数据分析支持强大的聚合查询能快速进行数据统计和分析高可用支持主从复制单个节点故障不影响服务可扩展可以轻松扩展到上百个节点处理 PB 级别的数据为什么不用 MySQL 做搜索很多人会问MySQL 也有 like 查询为什么还要用 ES答案很简单MySQL 的 like 查询在大数据量下性能极差而且不支持全文检索和复杂的数据分析。MySQL 的 like % 关键词 % 查询会走全表扫描百万级数据就会明显变慢MySQL 不支持分词只能做简单的字符串匹配MySQL 不支持复杂的相关性排序MySQL 不支持聚合分析和统计而 ES 就是为了解决这些问题而生的。它专门针对搜索和分析进行了优化能在毫秒级返回千万级数据的查询结果。二、ES 的核心应用场景ES 的应用场景非常广泛我总结了最常用的 8 个场景全文检索商品搜索、文章搜索、用户搜索日志分析ELKElasticsearchLogstashKibana日志收集分析系统指标监控系统指标、业务指标的实时监控和告警数据分析用户行为分析、销售数据分析、运营数据分析地理信息搜索附近的人、附近的商家、地理位置查询自动补全搜索框的输入提示、自动补全功能拼写纠错搜索时的拼写检查和纠错数据可视化配合 Kibana 制作各种数据仪表盘三、ES 的核心概念要真正用好 ES必须先搞懂它的核心概念。很多人学 ES 觉得难就是因为这些概念和关系型数据库不一样。我做了一个 ES 和 MySQL 的概念对比表帮助你快速理解表格ElasticsearchMySQLIndex索引Database数据库Document文档Row行Field字段Column列Mapping映射Table Schema表结构Query DSLSQLShard分片分表Replica副本主从复制核心概念详解Index索引ES 中存储数据的地方相当于 MySQL 的数据库。一个索引就是一个逻辑上的数据集合。Document文档ES 中的最小数据单元相当于 MySQL 的一行记录。文档以 JSON 格式存储。Field字段文档中的属性相当于 MySQL 的列。每个字段都有自己的数据类型。Mapping映射定义索引中字段的数据类型、分词器、索引方式等相当于 MySQL 的表结构。Shard分片将一个大的索引分成多个小的分片分布在不同的节点上实现水平扩展。Replica副本分片的备份用于提高数据的可用性和查询性能。四、Spring Boot 集成 ES 完整教程下面我以 Spring Boot 2.7.x 为例教你如何快速集成和使用 Elasticsearch。第一步引入依赖xmldependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-elasticsearch/artifactId /dependency第二步配置 ES 连接yamlspring: elasticsearch: rest: uris: http://localhost:9200 username: elastic password: 123456 connection-timeout: 5s read-timeout: 30s第三步定义实体类和 Mappingjava运行Data Document(indexName product, shards 3, replicas 1) public class Product { Id private Long 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.Double) private Double price; Field(type FieldType.Integer) private Integer stock; Field(type FieldType.Keyword) private String category; Field(type FieldType.Date, format DateFormat.date_time) private Date createTime; }第四步定义 Repository 接口java运行public interface ProductRepository extends ElasticsearchRepositoryProduct, Long { // 根据名称搜索 ListProduct findByName(String name); // 根据名称和描述搜索 ListProduct findByNameOrDescription(String name, String description); // 根据价格区间查询 ListProduct findByPriceBetween(Double minPrice, Double maxPrice); }第五步使用 Repository 操作 ESjava运行Service public class ProductService { Autowired private ProductRepository productRepository; // 保存商品 public void saveProduct(Product product) { productRepository.save(product); } // 根据ID查询 public Product findById(Long id) { return productRepository.findById(id).orElse(null); } // 搜索商品 public ListProduct search(String keyword) { return productRepository.findByNameOrDescription(keyword, keyword); } // 删除商品 public void deleteById(Long id) { productRepository.deleteById(id); } }第六步复杂查询Query DSL对于复杂的查询我们需要使用 ElasticsearchRestTemplate 来编写 Query DSLjava运行Service public class ProductSearchService { Autowired private ElasticsearchRestTemplate elasticsearchTemplate; public PageProduct searchProducts(String keyword, String category, Double minPrice, Double maxPrice, int pageNum, int pageSize) { NativeSearchQueryBuilder queryBuilder new NativeSearchQueryBuilder(); // 构建查询条件 BoolQueryBuilder boolQuery QueryBuilders.boolQuery(); // 关键词搜索 if (StringUtils.hasText(keyword)) { boolQuery.must(QueryBuilders.multiMatchQuery(keyword, name, description) .type(MultiMatchQueryBuilder.Type.BEST_FIELDS) .boost(3.0f)); } // 分类过滤 if (StringUtils.hasText(category)) { boolQuery.filter(QueryBuilders.termQuery(category, category)); } // 价格区间过滤 if (minPrice ! null maxPrice ! null) { boolQuery.filter(QueryBuilders.rangeQuery(price).gte(minPrice).lte(maxPrice)); } queryBuilder.withQuery(boolQuery); // 排序按价格升序 queryBuilder.withSort(SortBuilders.fieldSort(price).order(SortOrder.ASC)); // 分页 queryBuilder.withPageable(PageRequest.of(pageNum - 1, pageSize)); // 高亮显示 HighlightBuilder highlightBuilder new HighlightBuilder(); highlightBuilder.field(name).field(description); highlightBuilder.preTags(em stylecolor:red); highlightBuilder.postTags(/em); queryBuilder.withHighlightBuilder(highlightBuilder); // 执行查询 SearchHitsProduct searchHits elasticsearchTemplate.search( queryBuilder.build(), Product.class); // 处理高亮结果 ListProduct products searchHits.stream().map(hit - { Product product hit.getContent(); // 替换高亮字段 if (hit.getHighlightFields().containsKey(name)) { product.setName(hit.getHighlightFields().get(name).get(0)); } if (hit.getHighlightFields().containsKey(description)) { product.setDescription(hit.getHighlightFields().get(description).get(0)); } return product; }).collect(Collectors.toList()); return new PageImpl(products, PageRequest.of(pageNum - 1, pageSize), searchHits.getTotalHits()); } }五、深入原理ES 为什么这么快ES 的高性能是由多个关键技术共同决定的1. 倒排索引这是 ES 最核心的数据结构。和关系型数据库的正排索引不同倒排索引记录的是 关键词 - 文档 的映射关系。举个例子文档 1我爱 Java文档 2我爱 Elasticsearch文档 3Java 是最好的编程语言倒排索引就是plaintext我 → [1, 2] 爱 → [1, 2] Java → [1, 3] Elasticsearch → [2] 是 → [3] 最好的 → [3] 编程语言 → [3]当你搜索 Java 时ES 直接从倒排索引中找到包含 Java 的文档 1 和 3速度非常快。2. 分词器ES 会将文本内容拆分成一个个关键词然后建立倒排索引。分词器的质量直接决定了搜索结果的准确性。ES 内置了多种分词器最常用的中文分词器是 IK 分词器它支持细粒度分词ik_max_word和粗粒度分词ik_smart两种模式。3. 分布式架构ES 天生就是分布式的它将数据分成多个分片分布在不同的节点上。查询时可以并行查询多个分片然后合并结果大大提高了查询速度。4. 内存缓存ES 将热点数据和索引缓存到内存中查询时直接从内存获取避免了磁盘 IO。六、ES 常见问题与解决方案在生产环境中使用 ES最常见的问题有问题 1深分页问题问题描述当使用 fromsize 进行分页时from 越大查询越慢。当 from 超过 10000 时ES 会直接报错。原因ES 需要从所有分片中查询前 fromsize 条数据然后在协调节点进行排序和合并最后返回 size 条数据。from 越大这个过程越耗时。解决方案使用 search_after适用于滚动加载场景不支持跳页使用 scroll适用于批量导出数据不适用于用户交互限制分页深度业务上限制用户只能翻到前 100 页问题 2索引设计不当导致查询慢常见错误给不需要搜索的字段设置为 text 类型使用默认的分词器处理中文索引分片数设置不合理没有设置合理的刷新间隔解决方案只给需要搜索的字段设置为 text 类型其他字段设置为 keyword 或数值类型使用 IK 分词器处理中文分片数设置为节点数的 1-3 倍单个分片大小控制在 10-50GB将刷新间隔refresh_interval设置为 30s 或更长提高写入性能问题 3数据写入慢问题描述大量数据写入时ES 性能下降严重。解决方案使用批量写入bulk API每次批量写入 1000-5000 条数据关闭自动刷新写入完成后再手动刷新增加副本数为 0写入完成后再恢复副本使用 SSD 磁盘调整 JVM 堆内存大小设置为物理内存的一半不超过 32GB问题 4集群健康状态异常常见状态Green所有主分片和副本都正常运行Yellow所有主分片正常运行部分副本不正常Red部分主分片不正常解决方案Yellow 状态检查节点是否正常增加节点数Red 状态检查故障节点恢复数据必要时重建索引七、ES 生产环境最佳实践最后我分享几个我在生产环境中总结的 ES 最佳实践版本选择建议使用 7.x 版本不要使用太新或太旧的版本集群规划至少 3 个节点避免脑裂问题硬件配置使用 SSD 磁盘内存越大越好CPU 核心数不少于 4 核索引设计索引名使用小写字母用下划线分隔不要在一个索引中存储太多不同类型的数据按时间分索引比如每天一个索引方便管理和删除查询优化避免使用通配符开头的查询避免查询大量数据使用 filter 代替 query 进行过滤filter 会缓存结果只返回需要的字段不要返回整个文档安全配置开启身份认证和权限控制禁止外网直接访问 ES 集群监控告警监控集群健康状态、节点状态、索引大小、查询延迟等指标备份恢复定期备份数据防止数据丢失

更多文章