🧑 博主简介:历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,
15年
工作经验,精通Java编程
,高并发设计
,Springboot和微服务
,熟悉Linux
,ESXI虚拟化
以及云原生Docker和K8s
,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。
Spring Boot 结合 Elasticsearch 实现数据库与 ES 索引同步
在现代应用开发中,保持数据库与搜索引擎索引的同步是非常重要的。这样可以确保搜索结果的准确性和实时性。本文将介绍如何使用 Spring Boot 结合 Elasticsearch 实现数据库记录的更新和 ES 索引更新实时同步,并且能够周期性监测手动增删改数据库记录并同步更新到 ES 索引库。
一、技术方案
(一)实时同步
- 利用数据库的事件机制(如触发器、监听数据库日志等)或者在应用层通过 AOP(面向切面编程)拦截数据库操作,当数据库记录发生更新时,立即触发同步操作将更新同步到 Elasticsearch 索引中。
(二)周期性监测
- 启动一个定时任务,定期扫描数据库中的记录,与 Elasticsearch 索引中的数据进行对比。如果发现差异,进行同步更新操作。
二、技术选型
- Spring Boot:提供快速开发框架,方便集成各种组件。
- Elasticsearch:强大的搜索和分析引擎,用于构建高效的搜索功能。
- Spring Data Elasticsearch:简化与 Elasticsearch 的交互操作。
- 数据库(如 MySQL、PostgreSQL 等):存储应用数据。
三、实施步骤
(一)项目搭建
- 创建一个 Spring Boot 项目,并添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
(二)创建实体类
- 创建一个数据库实体类,例如:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class MyEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String field1;
private String field2;
// Getters and setters
}
- 创建一个对应的 Elasticsearch 实体类:
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@Data
@Document(indexName = "my_index", type = "my_type")
public class ElasticsearchEntity {
@Id
private Long id;
@Field(type = FieldType.Text)
private String field1;
@Field(type = FieldType.Text)
private String field2;
}
(三)创建 Repository 接口
- 创建数据库 Repository 接口:
import org.springframework.data.jpa.repository.JpaRepository;
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
}
- 创建 Elasticsearch Repository 接口:
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface ElasticsearchEntityRepository extends ElasticsearchRepository<ElasticsearchEntity, Long> {
}
(四)实现实时同步
- 使用
AOP
拦截数据库操作来实现实时同步。创建一个切面类:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DatabaseSyncAspect {
private final MyEntityRepository myEntityRepository;
private final ElasticsearchEntityRepository elasticsearchEntityRepository;
@Autowired
public DatabaseSyncAspect(MyEntityRepository myEntityRepository, ElasticsearchEntityRepository elasticsearchEntityRepository) {
this.myEntityRepository = myEntityRepository;
this.elasticsearchEntityRepository = elasticsearchEntityRepository;
}
@AfterReturning(pointcut = "execution(* org.springframework.data.jpa.repository.JpaRepository+.save(..))", returning = "result")
public void afterSave(JoinPoint joinPoint, Object result) {
if (result instanceof MyEntity) {
MyEntity myEntity = (MyEntity) result;
ElasticsearchEntity elasticsearchEntity = new ElasticsearchEntity();
elasticsearchEntity.setId(myEntity.getId());
elasticsearchEntity.setField1(myEntity.getField1());
elasticsearchEntity.setField2(myEntity.getField2());
elasticsearchEntityRepository.save(elasticsearchEntity);
}
}
}
这个切面类会在数据库保存操作成功返回后,将更新同步到 Elasticsearch 索引中。
(五)实现周期性监测
- 使用 Spring 的定时任务功能,创建一个定时任务来周期性地检查数据库与 ES 索引的差异。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class DatabaseSyncTask {
private final MyEntityRepository myEntityRepository;
private final ElasticsearchEntityRepository elasticsearchEntityRepository;
@Autowired
public DatabaseSyncTask(MyEntityRepository myEntityRepository, ElasticsearchEntityRepository elasticsearchEntityRepository) {
this.myEntityRepository = myEntityRepository;
this.elasticsearchEntityRepository = elasticsearchEntityRepository;
}
@Scheduled(fixedRate = 60000) // 每分钟执行一次
public void syncDatabaseToElasticsearch() {
Iterable<MyEntity> myEntities = myEntityRepository.findAll();
for (MyEntity myEntity : myEntities) {
ElasticsearchEntity elasticsearchEntity = elasticsearchEntityRepository.findById(myEntity.getId()).orElse(null);
if (elasticsearchEntity == null ||!elasticsearchEntity.getField1().equals(myEntity.getField1()) ||!elasticsearchEntity.getField2().equals(myEntity.getField2())) {
elasticsearchEntity = new ElasticsearchEntity();
elasticsearchEntity.setId(myEntity.getId());
elasticsearchEntity.setField1(myEntity.getField1());
elasticsearchEntity.setField2(myEntity.getField2());
elasticsearchEntityRepository.save(elasticsearchEntity);
}
}
}
}
四、测试输出
(一)数据准备
假设我们有以下数据库记录:
id | field1 | field2 |
---|---|---|
1 | Value1 | Value2 |
(二)实时同步测试
- 执行数据库更新操作:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MyEntityService {
private final MyEntityRepository myEntityRepository;
@Autowired
public MyEntityService(MyEntityRepository myEntityRepository) {
this.myEntityRepository = myEntityRepository;
}
public void updateEntity(Long id, String field1, String field2) {
MyEntity myEntity = myEntityRepository.findById(id).orElse(null);
if (myEntity!= null) {
myEntity.setField1(field1);
myEntity.setField2(field2);
myEntityRepository.save(myEntity);
}
}
}
调用这个服务方法更新数据库记录,比如:
myEntityService.updateEntity(1L, "NewValue1", "NewValue2");
- 检查 Elasticsearch 索引中的数据是否同步更新。可以通过查询 Elasticsearch Repository 或者直接使用 Elasticsearch 的客户端工具来查看。
如果一切正常,应该可以看到 Elasticsearch 索引中的对应记录也被更新为 field1 = NewValue1
和 field2 = NewValue2
。
(三)周期性监测测试
- 手动修改数据库记录,比如使用数据库客户端工具直接修改记录。
id | field1 | field2 |
---|---|---|
1 | AnotherValue1 | AnotherValue2 |
-
等待定时任务执行(假设定时任务每分钟执行一次)。
-
检查 Elasticsearch 索引中的数据是否同步更新。同样可以通过查询 Elasticsearch Repository 或者直接使用 Elasticsearch 的客户端工具来查看。
应该可以看到 Elasticsearch 索引中的记录也被更新为 field1 = AnotherValue1
和 field2 = AnotherValue2
。
五、总结
通过以上步骤,我们成功地实现了 Spring Boot 结合 Elasticsearch 实现数据库记录的更新和 ES 索引更新实时同步,以及通过工具手动增删改数据库记录也能周期性监测到并同步更新到 ES 索引库。这种方式可以确保搜索功能始终保持最新的数据,提高应用的性能和用户体验。在实际应用中,可以根据具体需求调整定时任务的执行频率和同步策略,以满足不同场景的要求。