Spring Boot 与 Elasticsearch 8.x 集成

以下是针对 Spring Boot 与 Elasticsearch 8.x 集成的更详细最佳实践,涵盖配置优化、性能调优、安全、测试等关键方面。


1. 版本与依赖管理

1.1 明确版本关系

  • Spring Boot 3.x + Spring Data Elasticsearch 5.x + Elasticsearch 8.x 是官方推荐组合。

  • 检查依赖树,避免冲突:

    xml 复制代码
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>
    <!-- 确保使用 Elasticsearch 8.x 的 Java Client -->
    <dependency>
        <groupId>co.elastic.clients</groupId>
        <artifactId>elasticsearch-java</artifactId>
        <version>8.12.0</version>
    </dependency>

1.2 排除旧客户端

确保不引入 RestHighLevelClient

xml 复制代码
<exclusions>
    <exclusion>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>elasticsearch-rest-high-level-client</artifactId>
    </exclusion>
</exclusions>

2. 配置与连接管理

2.1 基础配置(application.yml

yaml 复制代码
spring:
  elasticsearch:
    uris: ["https://elasticsearch-host:9200"]  # Elasticsearch 8 默认启用 HTTPS
    username: "elastic"
    password: "your_password"
    ssl:
      certificate-authorities: "/path/to/http_ca.crt"  # 必须配置 CA 证书
    connection-timeout: 5s
    socket-timeout: 30s
    max-connections: 100

2.2 自定义配置类

typescript 复制代码
@Configuration
public class ElasticsearchConfig {
​
    @Bean
    public ElasticsearchClient elasticsearchClient(RestClient restClient) {
        return new ElasticsearchClient(new RestClientTransport(restClient, new JacksonJsonpMapper()));
    }
​
    @Bean
    public RestClient restClient(@Value("${spring.elasticsearch.uris}") String[] uris) {
        return RestClient.builder(HttpHost.create(uris[0]))
            .setHttpClientConfigCallback(httpClientBuilder -> {
                // 配置 SSL 上下文
                SSLContext sslContext = loadSSLContext();
                return httpClientBuilder.setSSLContext(sslContext);
            })
            .build();
    }
}

3. 索引与映射设计

3.1 显式定义索引映射

less 复制代码
@Document(indexName = "orders", createIndex = false)  // 禁止自动创建索引
@Setting(
    dynamic = Dynamic.STRICT,  // 禁止未定义的字段
    numberOfShards = 3,
    numberOfReplicas = 1
)
public class Order {
    @Id
    private String id;
​
    @Field(type = FieldType.Keyword)
    private String orderId;
​
    @Field(type = FieldType.Text, analyzer = "ik_smart")
    private String description;
​
    @Field(type = FieldType.Date, format = DateFormat.date_optional_time)
    private LocalDateTime orderDate;
}

3.2 使用 Index Templates 管理动态字段

bash 复制代码
PUT _index_template/order_template
{
  "index_patterns": ["orders*"],
  "template": {
    "settings": {
      "number_of_shards": 3,
      "analysis": {
        "analyzer": {
          "ik_smart": { "type": "ik_smart" }
        }
      }
    },
    "mappings": {
      "dynamic": "strict",
      "properties": {
        "orderId": { "type": "keyword" },
        "description": { "type": "text", "analyzer": "ik_smart" }
      }
    }
  }
}

4. 数据操作与查询优化

4.1 使用 Repository 简化操作

typescript 复制代码
public interface OrderRepository extends ElasticsearchRepository<Order, String> {
    // 自动生成 DSL
    List<Order> findByDescription(String description);
}

4.2 复杂查询使用 ElasticsearchClient

scss 复制代码
@Autowired
private ElasticsearchClient elasticsearchClient;
​
public List<Order> searchOrders(String keyword) {
    Query query = Query.of(q -> q
        .bool(b -> b
            .must(m -> m.match(t -> t
                .field("description")
                .query(keyword)
            ))
        )
    );
​
    SearchResponse<Order> response = elasticsearchClient.search(s -> s
        .index("orders")
        .query(query)
        .size(100), Order.class);
​
    return response.hits().hits().stream()
        .map(Hit::source)
        .collect(Collectors.toList());
}

4.3 批量写入优化

less 复制代码
BulkRequest.Builder bulkRequest = new BulkRequest.Builder();
orders.forEach(order -> bulkRequest
    .operations(op -> op
        .index(idx -> idx
            .index("orders")
            .id(order.getId())
            .document(order)
        )
    )
);
elasticsearchClient.bulk(bulkRequest.build());

5. 性能调优

5.1 分片与副本策略

  • 分片数:根据数据量估算,单个分片建议不超过 50GB。
  • 副本数:生产环境至少 1 个副本,高可用场景可设置为 2。

5.2 刷新间隔与写入优化

scss 复制代码
// 创建索引时配置
CreateIndexRequest request = new CreateIndexRequest.Builder()
    .index("orders")
    .settings(s -> s
        .refreshInterval("30s")  // 降低刷新频率
        .numberOfShards("3")
    )
    .build();
elasticsearchClient.indices().create(request);

5.3 查询性能优化

  • 使用 filter 代替 query 上下文加速非相关性查询:

    less 复制代码
    Query query = Query.of(q -> q
        .bool(b -> b
            .filter(f -> f.term(t -> t.field("status").value("completed"))
        )
    );
  • 避免 wildcard 查询,改用 edge_ngram 分词器。


6. 安全与认证

6.1 强制 HTTPS 和 TLS

  • 生成 CA 证书并配置 Elasticsearch:

    yaml 复制代码
    xpack.security.enabled: true
    xpack.security.http.ssl:
      enabled: true
      keystore.path: certs/elastic-certificates.p12
      truststore.path: certs/elastic-certificates.p12

6.2 角色权限控制

  • elasticsearch.yml 中定义最小权限角色:

    yaml 复制代码
    xpack.security.authz:
      roles:
        app_user:
          indices:
            - names: ['orders*']
              privileges: ['read', 'index']

7. 异常处理与重试

7.1 自定义异常处理

java 复制代码
@ControllerAdvice
public class ElasticsearchExceptionHandler {
​
    @ExceptionHandler(ElasticsearchException.class)
    public ResponseEntity<ErrorResponse> handleEsException(ElasticsearchException ex) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body(new ErrorResponse("ES_ERROR", ex.getMessage()));
    }
}

7.2 重试机制(结合 Resilience4j)

typescript 复制代码
@Bean
public RetryConfig retryConfig() {
    return RetryConfig.custom()
        .maxAttempts(3)
        .waitDuration(Duration.ofMillis(500))
        .retryOnException(e -> e instanceof ElasticsearchException)
        .build();
}
​
@Retry(name = "elasticsearchRetry")
public void saveOrder(Order order) {
    orderRepository.save(order);
}

8. 监控与运维

8.1 集成 Prometheus + Grafana

  • 配置 Elasticsearch Exporter:

    yaml 复制代码
    metrics.enabled: true
    xpack.monitoring.exporters:
      prometheus:
        type: prometheus
        host: ["localhost:9200"]

8.2 日志记录

  • 记录慢查询(elasticsearch.yml):

    diff 复制代码
    index.search.slowlog.threshold.query.warn: 10s
    index.search.slowlog.threshold.fetch.debug: 500ms

9. 测试策略

9.1 使用 Testcontainers 进行集成测试

less 复制代码
@Testcontainers
@SpringBootTest
public class OrderRepositoryTest {
​
    @Container
    static ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.12.0")
        .withPassword("password")
        .withExposedPorts(9200);
​
    @DynamicPropertySource
    static void setProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.elasticsearch.uris", () -> "https://" + container.getHost() + ":" + container.getMappedPort(9200));
        registry.add("spring.elasticsearch.username", () -> "elastic");
        registry.add("spring.elasticsearch.password", () -> "password");
        registry.add("spring.elasticsearch.ssl.certificate-authorities", () -> "/path/to/http_ca.crt");
    }
}

10. 高级特性

10.1 向量搜索(Elasticsearch 8 新增)

less 复制代码
@Field(type = FieldType.DenseVector, dims = 512)
private float[] embedding;
​
// 查询时使用 knn 搜索
Query query = Query.of(q -> q
    .knn(k -> k
        .field("embedding")
        .queryVector(embeddingArray)
        .k(10)
        .numCandidates(100)
    )
);

10.2 索引生命周期管理(ILM)

bash 复制代码
PUT _ilm/policy/orders_policy
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": { "max_size": "50GB" }
        }
      },
      "delete": {
        "min_age": "30d",
        "actions": { "delete": {} }
      }
    }
  }
}

总结:核心注意事项

  1. 安全第一:强制 HTTPS、配置 CA 证书、最小权限原则。
  2. 索引设计:显式定义映射、合理分片、使用模板管理动态索引。
  3. 性能调优:批量写入、调整刷新间隔、避免深分页。
  4. 容错机制:全局异常处理、重试策略、熔断降级。
  5. 监控告警:集成 Prometheus、记录慢查询日志。

通过以上实践,可构建高性能、高可用的 Elasticsearch 8 集成方案,适配生产级需求。

相关推荐
一次旅行1 天前
【效率秘籍】Shell 函数+别名组合拳,让 80% 的重复命令变 1 个字母
大数据·elasticsearch·搜索引擎
暖和_白开水1 天前
数据分析agent(九):es_client_manager.py 和分词器ik_max_word问题
elasticsearch·数据分析·c#
码农学院1 天前
Elasticsearch构建汽车零配件智能搜索与匹配系统
大数据·elasticsearch·汽车
Elastic 中国社区官方博客2 天前
不到 5 分钟完成本地部署:Jina embedding 模型现已支持本地部署
大数据·人工智能·elasticsearch·搜索引擎·embedding·jina
二进制流水搬运工2 天前
入职第一天拉了23个仓库,我写了个脚本解放双手
大数据·elasticsearch·搜索引擎
Elasticsearch2 天前
在不到一小时内将 Datadog Kubernetes 仪表板迁移到 Elastic Observability
elasticsearch
.柒宇.2 天前
Elasticsearch 核心概念与系统架构详解
elasticsearch·系统架构
风行無痕2 天前
单机Elasticsearch 8.19.18安全升级到9.4.3的过程
大数据·elasticsearch·搜索引擎
Elastic 中国社区官方博客2 天前
使用重新设计的 AutoOps 更快地进行 Elasticsearch 问题排查
大数据·运维·前端·人工智能·elasticsearch·搜索引擎·全文检索
阿拉雷️2 天前
【搜索实战】Spring Boot 3.3 + AI Agent × Elasticsearch:让AI自动优化搜索策略,查询速度从2秒压到50毫秒
人工智能·spring boot·elasticsearch