首页 区块链

Elasticsearch Java 实战:从零构建高性能搜索服务

分类:区块链
字数: (0879)
阅读: (7764)
内容摘要:Elasticsearch Java 实战:从零构建高性能搜索服务,

在互联网应用中,搜索功能至关重要。传统的关系型数据库在面对海量数据和复杂查询时显得力不从心。这时,Elasticsearch 便能大显身手。本文将深入探讨 Elasticsearch 的核心概念,并结合 Java 实践,带你从入门到落地,打造高性能的搜索服务。

问题场景重现:传统数据库搜索瓶颈

假设一个电商平台,商品数量巨大,用户需要通过关键词快速找到想要的商品。使用 MySQL 的 LIKE 查询,当数据量达到百万级别时,性能会急剧下降。即使添加索引,也无法满足高并发、低延迟的需求。此外,模糊匹配、拼写纠错等高级搜索功能也难以实现。这就是传统数据库在全文检索场景下的痛点。

Elasticsearch 底层原理剖析:倒排索引的魅力

Elasticsearch 之所以能够实现高效搜索,核心在于其采用的倒排索引(Inverted Index)。

1. 倒排索引的概念:

Elasticsearch Java 实战:从零构建高性能搜索服务

与传统数据库的正向索引不同,倒排索引以关键词为索引,记录包含该关键词的文档 ID。例如:

文档1: "Elasticsearch 是一个分布式搜索和分析引擎"
文档2: "Java 语言用于构建各种应用程序"
文档3: "Elasticsearch 可以与 Java 集成"

倒排索引如下:

Elasticsearch -> [1, 3]
是 -> [1]
一个 -> [1]
分布式 -> [1]
搜索 -> [1]
和 -> [1]
分析 -> [1]
引擎 -> [1]
Java -> [2, 3]
语言 -> [2]
用于 -> [2]
构建 -> [2]
各种 -> [2]
应用程序 -> [2]
可以 -> [3]
与 -> [3]
集成 -> [3]

2. Lucene:Elasticsearch 的引擎:

Elasticsearch Java 实战:从零构建高性能搜索服务

Elasticsearch 基于 Apache Lucene 构建,Lucene 提供了倒排索引的底层实现,包括分词、索引构建、搜索算法等。

3. 分布式架构:

Elasticsearch 是一个分布式系统,可以将数据分散存储在多个节点上,提高系统的吞吐量和可用性。Elasticsearch 使用 Shard 和 Replica 机制实现数据的分片和备份,保证数据安全和查询效率。集群管理方面,Elasticsearch 通过 Zen Discovery 进行节点发现和选举 Master 节点。常用的集群部署方式包括单节点部署、多节点部署以及基于 Docker 的容器化部署。

Elasticsearch Java 实战:从零构建高性能搜索服务

Java 集成 Elasticsearch:代码实战

1. 添加 Maven 依赖:

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.17.0</version>  <!-- 选择与 Elasticsearch 服务端版本匹配的版本 -->
</dependency>

2. 连接 Elasticsearch 集群:

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;

public class ElasticsearchClient {

    private static RestHighLevelClient client;

    public static RestHighLevelClient getInstance() {
        if (client == null) {
            client = new RestHighLevelClient(
                    RestClient.builder(
                            new HttpHost("localhost", 9200, "http") // Elasticsearch 服务地址
                    )
            );
        }
        return client;
    }

    public static void close() throws Exception {
        if (client != null) {
            client.close();
        }
    }

    public static void main(String[] args) throws Exception {
        RestHighLevelClient client = ElasticsearchClient.getInstance();
        System.out.println("连接 Elasticsearch 成功!");
        ElasticsearchClient.close();
    }
}

3. 创建索引:

Elasticsearch Java 实战:从零构建高性能搜索服务
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentType;

import java.io.IOException;

public class IndexOperations {

    public static void createIndex(String indexName) throws IOException {
        CreateIndexRequest request = new CreateIndexRequest(indexName);
        // 设置索引的 settings,例如分片数、副本数
        request.settings(Settings.builder()
                .put("index.number_of_shards", 3) // 设置分片数
                .put("index.number_of_replicas", 1) // 设置副本数
        );

        // 设置索引的 mappings,定义字段类型
        request.mapping("{
                \"properties\": {
                    \"title\": { \"type\": \"text\" },
                    \"content\": { \"type\": \"text\" },
                    \"price\": { \"type\": \"double\" }
                }
            }", XContentType.JSON);

        CreateIndexResponse createIndexResponse = ElasticsearchClient.getInstance().indices().create(request, RequestOptions.DEFAULT);
        System.out.println("创建索引结果:" + createIndexResponse.isAcknowledged());
    }

    public static void main(String[] args) throws IOException {
        createIndex("products"); // 索引名称
    }
}

4. 索引文档:

import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.common.xcontent.XContentType;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class DocumentOperations {

    public static void indexDocument(String indexName, String documentId, Map<String, Object> data) throws IOException {
        IndexRequest request = new IndexRequest(indexName);
        request.id(documentId); // 文档 ID
        request.source(data, XContentType.JSON);

        IndexResponse indexResponse = ElasticsearchClient.getInstance().index(request, RequestOptions.DEFAULT);
        System.out.println("索引文档结果:" + indexResponse.getResult());
    }

    public static void main(String[] args) throws IOException {
        Map<String, Object> product = new HashMap<>();
        product.put("title", "小米 13 Pro");
        product.put("content", "骁龙8 Gen2 处理器,徕卡影像");
        product.put("price", 4999.0);
        indexDocument("products", "1", product); // 索引名称,文档 ID,文档数据
    }
}

5. 执行搜索:

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;

import java.io.IOException;

public class SearchOperations {

    public static void search(String indexName, String keyword) throws IOException {
        SearchRequest searchRequest = new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 使用 match 查询,根据关键词进行搜索
        sourceBuilder.query(QueryBuilders.matchQuery("title", keyword));
        searchRequest.source(sourceBuilder);

        SearchResponse searchResponse = ElasticsearchClient.getInstance().search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = searchResponse.getHits();
        System.out.println("搜索结果数量:" + hits.getTotalHits().value);

        for (SearchHit hit : hits) {
            System.out.println("文档 ID:" + hit.getId());
            System.out.println("文档内容:" + hit.getSourceAsString());
        }
    }

    public static void main(String[] args) throws IOException {
        search("products", "小米"); // 索引名称,关键词
    }
}

实战避坑经验总结

  1. 版本兼容性: Elasticsearch 客户端和服务端版本必须兼容,否则可能出现连接或功能异常。
  2. 索引 Mapping 设计: 合理的 Mapping 设计至关重要,直接影响搜索性能和结果。要根据实际业务需求选择合适的字段类型和分词器。
  3. 分页查询性能: 深度分页(例如查询 10000+ 条数据)会导致性能问题。建议使用 Scroll API 或 Search After API 进行分页。
  4. 集群监控: 使用 Elasticsearch Head、Kibana 或 Prometheus + Grafana 等工具对集群进行监控,及时发现和解决问题。
  5. 性能优化: 针对具体业务场景,可以进行性能优化,例如调整分片数、副本数,使用 Bulk API 批量索引数据,优化查询语句等。

在实际生产环境中,我们还需要考虑安全性、高可用性、备份恢复等方面的问题。例如,可以使用 Shield 或 X-Pack 进行权限控制,使用 Curator 或 ZooKeeper 进行集群管理,使用 Snapshot API 进行数据备份。

此外,在服务器部署方面,可以使用 Nginx 作为反向代理,实现负载均衡,提高系统的并发连接数。对于小型项目,也可以考虑使用宝塔面板简化服务器管理。

掌握了以上 Elasticsearch 基础知识和 Java 实战技巧,相信你能够轻松应对各种搜索场景,打造高性能的搜索服务。

Elasticsearch Java 实战:从零构建高性能搜索服务

转载请注明出处: 加班到秃头

本文的链接地址: http://m.acea1.store/blog/045379.SHTML

本文最后 发布于2026-04-16 11:47:18,已经过了11天没有更新,若内容或图片 失效,请留言反馈

()
您可能对以下文章感兴趣
评论
  • 武汉热干面 2 天前
    ES 的版本选择确实是个坑,之前就因为版本不兼容导致各种问题,血泪教训啊!
  • 背锅侠 21 小时前
    文章写得真不错,解决了我在项目中遇到的 Elasticsearch 性能问题,感谢!
  • 柠檬精 5 天前
    ES 的版本选择确实是个坑,之前就因为版本不兼容导致各种问题,血泪教训啊!