目录
背景
随着 Elasticsearch 8.x 的发布,公司决定将现有的 Elasticsearch 7.x 版本升级到 8.x。然而,在升级过程中,我们发现许多 API 和功能发生了不兼容的变化,导致系统在迁移过程中遇到了大量问题。虽然官方文档提供了基本的操作指南,但实际应用中涉及的细节和调整却并未得到充分覆盖。为了帮助大家更顺利地过渡到 8.x,并有效应对这些变化, 本文将详细探讨 Elasticsearch 8.x 与 7.x 版本之间的主要差异,特别是在 Java 开发中的实际应用与迁移问题。
版本区别
依赖差异
- 版本7.x以及更早之前版本依赖
XML
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.x.x</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.x.x</version>
</dependency>
- 8.x版本依赖
XML
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.x.x</version>
</dependency>
**安全性增强 :**与 7.x 的默认无安全配置不同,8.x 默认启用安全功能,包括用户认证、授权、TLS 加密等,给开发者带来了更多的配置和管理任务。
查询和聚合优化:某些查询接口和聚合方法发生了变化,特别是对于大规模数据集的支持和性能提升方面有了显著进步,但这些变化需要开发者重新调整代码实现。
安装elaticsearch8.x服务
本文试例在window系统本地安装服务
启动es服务
elaticsearch版本为:8.16
官方下载地址:Past Releases of Elastic Stack Software | Elastic
百度云网盘:百度网盘 请输入提取码 提取码: 92h6
下载完解压目录如下
进入config目录修改yml文件
因为8.x版本默认开启权限校验以及ssl证书校验,为了方便后续使用,这里只保留权限校验,关闭ssl证书校验
进入bin目录执行cmd打开命令行重置登录密码
.\elasticsearch-reset-password -u elastic
默认账号:elastic
记得保存好密码,以免遗忘
进入bin目录双击elasticsearch.bat启动es服务
安装es管理平台
- 可自行安装es官方面板:kibana
- 本文使用的是google浏览器插件:Multi Elasticsearch Heads(可在google插件市场下载)
打开插件并连接es服务
连接成功
项目集成
这里通过集成在spring boot项目中来了解es8.x的配置连接以及api的使用
注:虽然基于springboot,但是我们这里不使用 spring boot data提供的es集成依赖
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<version>x.x.x</version>
</dependency>
因为data只提供了一些es简单操作的api,对于复杂的只有通过script手写DSL语句来完成,扩展性和维护性都不是很好,对不熟悉DSL语句的也很不友好,所以我们还是选择使用原生依赖 elasticsearch-java
可下载博主github源码参考: GitHub - RemainderTime/spring-boot-base-demo: 拿来即用springboot基础框架项目
pom.xml文件引入依赖
XML
<!-- elasticsearch8.x 搜索引擎 -->
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.16.0</version>
</dependency>
application.yml配置
bash
elasticsearch:
host: localhost
port: 9200
username: elastic
password: 8wVPrsP=9vlQWHBuHniH #window系统本地启动 es8.x 重置密码命令:.\elasticsearch-reset-password -u elastic
ES初始化配置类实现
java
@Component
public class EsConfig {
@Value("${elasticsearch.host}")
private String elasticsearchHost;
@Value("${elasticsearch.port}")
private int elasticsearchPort;
@Value("${elasticsearch.username}")
private String username;
@Value("${elasticsearch.password}")
private String password;
/**
-最大连接数 (maxConnTotal):设置总的最大连接数,取决于业务的并发量。500-2000 之间较为合理。
-每个节点的最大连接数 (maxConnPerRoute):控制每个节点的最大连接数,建议 50-100 之间。
-IO 线程数 (setIoThreadCount):根据 CPU 核心数设置,通常为 2-4 倍 CPU 核心数。
-连接超时、套接字超时、获取连接超时:一般设置为 10-30 秒,复杂查询或大数据量操作可适当增加到 20-60 秒。
-失败监听器 (setFailureListener):自定义重试和故障处理逻辑,确保高可用性。
*/
@Bean
public ElasticsearchClient elasticsearchClient() {
// 创建凭证提供者
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials(username, password)
);
// 自定义 RestClientBuilder 配置
RestClientBuilder restClientBuilder = RestClient.builder(
new HttpHost(elasticsearchHost, elasticsearchPort, "http")
).setHttpClientConfigCallback(httpClientBuilder ->
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider) // 配置认证信息
);
// 配置连接超时、套接字超时、获取连接超时
restClientBuilder.setRequestConfigCallback(builder ->
builder.setConnectTimeout(20000)
.setSocketTimeout(20000)
.setConnectionRequestTimeout(20000)
);
// 创建 RestClientTransport 和 ElasticsearchClient
RestClient restClient = restClientBuilder.build();
ElasticsearchTransport transport = new RestClientTransport(
restClient,
new JacksonJsonpMapper() // 使用 Jackson 进行 JSON 处理
);
return new ElasticsearchClient(transport);
}
}
ES8.x常用API实现
先创建一个全局es工具类
java
@Slf4j
@Component
public class EsUtil {
public static ElasticsearchClient esClient;
{
esClient = (ElasticsearchClient) ApplicationContextUtils.getBean("elasticsearchClient");
}
/...
}
下面罗列工具类中实现的常用的操作方法。
1.判断es索引是否存在
java
public static boolean existIndex(String indexName) {
try {
// 创建 ExistsRequest 请求
ExistsRequest request = new ExistsRequest.Builder()
.index(indexName)
.build();
// 发送请求并获取响应
BooleanResponse response = esClient.indices().exists(request);
// 返回索引是否存在
return response.value();
} catch (Exception e) {
// 处理异常
e.printStackTrace();
return false;
}
}
2.删除索引
java
@SneakyThrows
public static void delIndex(String indexName) {
if (existIndex(indexName)) {
return;
}
esClient.indices().delete(d -> d.index(indexName));
}
3.创建索引
java
public static void createIndex(String indexName) {
if (existIndex(indexName)) {
throw new RuntimeException("索引已经存在");
}
try {
CreateIndexResponse createIndexResponse = esClient.indices().create(c -> c.index(indexName));
// 处理响应
if (createIndexResponse.acknowledged()) {
log.info(" indexed create successfully.");
} else {
log.info("Failed to create index.");
}
} catch (Exception e) {
// 捕获异常并打印详细错误信息
e.printStackTrace();
throw new RuntimeException("创建索引失败,索引名:" + indexName + ",错误信息:" + e.getMessage(), e);
}
}
4.新增文档
java
public static boolean addDocument(EsBaseModel esBaseModel) {
try {
// 创建 IndexRequest 实例
IndexRequest request = new IndexRequest.Builder()
.index(esBaseModel.getIndexName())
.id(esBaseModel.getDocumentId()) //指定文档id,不指定会自动生成
.document(esBaseModel.getDocumentModel())
.opType(OpType.Create) // 只会在文档 ID 不存在时创建文档
.build();
IndexResponse response = esClient.index(request);
if ("created".equals(response.result())) {
log.info("Document created: " + response.id());
return true;
} else {
log.info("Document already exists or failed to create.");
return false;
}
} catch (Exception e) {
log.error("es新增文档失败", e);
e.printStackTrace();
}
return false;
}
5.更新文档
java
public boolean updateDocument(EsBaseModel esBaseModel) {
try {
UpdateRequest updateRequest = new UpdateRequest.Builder<>()
.index(esBaseModel.getIndexName())
.id(esBaseModel.getDocumentId())
.doc(esBaseModel.getDocumentModel()).build();
UpdateResponse updateResponse = esClient.update(updateRequest, esBaseModel.getClazz());
log.info("Document updated: " + updateResponse.id());
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
6.根据id查询文档
java
public static <T> T getDocumentById(EsBaseModel esBaseModel) {
try {
GetRequest getRequest = new GetRequest.Builder()
.index(esBaseModel.getIndexName())
.id(esBaseModel.getDocumentId())
.build();
GetResponse<T> getResponse = esClient.get(getRequest, esBaseModel.getClazz());
if (getResponse.found()) {
return getResponse.source();
}
} catch (Exception e) {
log.error("es列表查询失败", e);
}
return null;
}
7.根据id删除文档
java
public static Boolean deleteDocumentById(EsBaseModel esBaseModel) {
try {
DeleteRequest deleteRequest = new DeleteRequest.Builder()
.index(esBaseModel.getDocumentId())
.id(esBaseModel.getDocumentId())
.build();
DeleteResponse deleteResponse = esClient.delete(deleteRequest);
if ("deleted".equals(deleteResponse.result())) {
log.info("Document deleted: " + deleteResponse.id());
return true;
} else {
log.info("Document delete failed: " + deleteResponse.id());
return false;
}
} catch (Exception e) {
log.error("es列表删除失败", e);
}
return false;
}
8.查询文档列表
java
public static <T> List<T> getDocumentList(EsSearchModel searchModel) {
List<T> eslist = new ArrayList<>();
try {
SearchResponse<T> search = esClient.search(buildSearchRequest(searchModel), searchModel.getClazz());
if (Objects.isNull(search)) {
return eslist;
}
HitsMetadata<T> hits = search.hits();
if (Objects.isNull(hits)) {
return eslist;
}
List<Hit<T>> sourceHitList = hits.hits();
if (CollectionUtils.isEmpty(sourceHitList)) {
return eslist;
}
sourceHitList.forEach(item -> {
// 处理每个命中
eslist.add(item.source());
});
return eslist;
} catch (Exception e) {
log.error("es列表查询失败", e);
}
return eslist;
}
注意!!!由于方法太多,文中就不一一列举,想了解更多方法可下载上面博主提供的github地址进行下载项目并找到EsUtil.class类查看,里面有更多场景的使用方法和api详解
定义接口创建索引
定义业务方法
java
@Slf4j
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public RetObj syncEs(Long userId) {
User user = userMapper.selectById(userId);
if (Objects.isNull(user)) {
return RetObj.error("用户不存在");
}
String index = StringUtil.camelToKebabCase(user.getClass().getSimpleName());
if (!EsUtil.existIndex(index)) {
EsUtil.createIndex(index);
}
EsUtil.addDocument(new EsBaseModel(index, String.valueOf(user.getId()), user, user.getClass()));
return RetObj.success();
}
@Override
public RetObj getEsId(Long userId) {
Object user = EsUtil.getDocumentById(new EsBaseModel("user", String.valueOf(userId), null, User.class));
if(Objects.nonNull(user)){
return RetObj.success(user);
}
return RetObj.error("es中不存在该用户");
}
}
定义请求接口
java
@RestController(value = "用户控制器")
@RequestMapping("/user")
@Tag(name = "用户控制器")
public class UserController {
@Autowired
private UserService userService;
@Operation(summary = "es同步用户信息", description = "用户信息")
@GetMapping("/syncEs")
public RetObj syncEs(Long userId){
return userService.syncEs(userId);
}
@Operation(summary = "es查询用户信息", description = "用户信息")
@GetMapping("/getEsId")
public RetObj getEsId(Long userId){
return userService.getEsId(userId);
}
}
操作接口
请求接口插入一条数据到索引中
查看面板发现自动创建了一个user索引
查询user索引中的数据存在一条
请求接口查询es数据
至此对于Elaticsearch8.x版本的学习完成了