布莱克庄园降神会免安装绿色版
655M · 2025-11-14
本文面向有一定 Spring Boot 开发经验的 Java 程序员,将详细介绍如何在现有 Spring Boot + MySQL 技术栈中集成 ElasticSearch,实现高效的数据搜索功能。
ElasticSearch 是一个基于 Lucene 构建的开源、分布式、RESTful 搜索引擎。它提供了一个分布式多用户能力的全文搜索引擎,能够处理大规模数据的实时搜索和分析需求。
核心特性:
在传统的关系型数据库(如 MySQL)中,面对以下场景时会遇到瓶颈:
xml
<!-- Spring Boot Starter Data Elasticsearch -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Spring Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
yaml
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/blog_db
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
elasticsearch:
uris: http://localhost:9200
connection-timeout: 10s
socket-timeout: 30s
# 自定义配置
app:
elasticsearch:
index-settings:
number-of-shards: 3
number-of-replicas: 1
java
@Entity
@Table(name = "articles")
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 500)
private String title;
@Column(columnDefinition = "TEXT")
private String content;
@Column(name = "author_id")
private Long authorId;
@Column(name = "create_time")
private LocalDateTime createTime;
@Column(name = "update_time")
private LocalDateTime updateTime;
// getters and setters
}
java
@Document(indexName = "articles")
public class ArticleDocument {
@Id
private String id;
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
private String title;
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
private String content;
@Field(type = FieldType.Long)
private Long authorId;
@Field(type = FieldType.Keyword)
private String authorName;
@Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second)
private LocalDateTime createTime;
// getters and setters
}
java
@Service
@Transactional
public class ArticleService {
private final ArticleRepository articleRepository;
private final ArticleSearchRepository articleSearchRepository;
public ArticleService(ArticleRepository articleRepository,
ArticleSearchRepository articleSearchRepository) {
this.articleRepository = articleRepository;
this.articleSearchRepository = articleSearchRepository;
}
public Article createArticle(Article article) {
// 保存到 MySQL
Article savedArticle = articleRepository.save(article);
// 同步到 ElasticSearch
ArticleDocument document = convertToDocument(savedArticle);
articleSearchRepository.save(document);
return savedArticle;
}
public void updateArticle(Long id, Article article) {
// 更新 MySQL
article.setId(id);
Article updatedArticle = articleRepository.save(article);
// 更新 ElasticSearch
ArticleDocument document = convertToDocument(updatedArticle);
articleSearchRepository.save(document);
}
private ArticleDocument convertToDocument(Article article) {
ArticleDocument document = new ArticleDocument();
document.setId(article.getId().toString());
document.setTitle(article.getTitle());
document.setContent(article.getContent());
document.setAuthorId(article.getAuthorId());
document.setCreateTime(article.getCreateTime());
return document;
}
}
对于存量数据或大数据量场景,建议使用 Logstash 进行数据同步:
ruby
# mysql-to-es.conf
input {
jdbc {
jdbc_driver_library => "/path/to/mysql-connector-java-8.0.23.jar"
jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
jdbc_connection_string => "jdbc:mysql://localhost:3306/blog_db"
jdbc_user => "root"
jdbc_password => "password"
schedule => "* * * * *"
statement => "SELECT id, title, content, author_id, create_time FROM articles WHERE update_time > :sql_last_value"
use_column_value => true
tracking_column => "update_time"
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
index => "articles"
document_id => "%{id}"
}
}
java
public interface ArticleSearchRepository extends ElasticsearchRepository<ArticleDocument, String> {
// 简单查询
List<ArticleDocument> findByTitle(String title);
// 分词搜索
List<ArticleDocument> findByTitleContainingOrContentContaining(String title, String content);
// 使用 @Query 注解自定义查询
@Query("""
{
"bool": {
"should": [
{ "match": { "title": "?0" } },
{ "match": { "content": "?0" } }
]
}
}
""")
List<ArticleDocument> findByCustomQuery(String keyword);
// 分页查询
Page<ArticleDocument> findByTitleContaining(String title, Pageable pageable);
}
java
@Service
public class ArticleSearchService {
private final ElasticsearchRestTemplate elasticsearchTemplate;
public ArticleSearchService(ElasticsearchRestTemplate elasticsearchTemplate) {
this.elasticsearchTemplate = elasticsearchTemplate;
}
public SearchHits<ArticleDocument> advancedSearch(ArticleSearchRequest request) {
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 构建布尔查询
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
if (StringUtils.hasText(request.getKeyword())) {
boolQuery.must(QueryBuilders.multiMatchQuery(request.getKeyword(), "title", "content")
.analyzer("ik_smart"));
}
if (request.getAuthorId() != null) {
boolQuery.filter(QueryBuilders.termQuery("authorId", request.getAuthorId()));
}
if (request.getStartTime() != null && request.getEndTime() != null) {
boolQuery.filter(QueryBuilders.rangeQuery("createTime")
.gte(request.getStartTime())
.lte(request.getEndTime()));
}
queryBuilder.withQuery(boolQuery);
// 高亮显示
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("title").preTags("<em>").postTags("</em>");
highlightBuilder.field("content").preTags("<em>").postTags("</em>");
queryBuilder.withHighlightBuilder(highlightBuilder);
// 分页和排序
queryBuilder.withPageable(PageRequest.of(request.getPage(), request.getSize()));
queryBuilder.withSort(SortBuilders.scoreSort());
NativeSearchQuery searchQuery = queryBuilder.build();
return elasticsearchTemplate.search(searchQuery, ArticleDocument.class);
}
}
索引 (Index)
文档 (Document)
分片 (Shard)
映射 (Mapping)
Match Query
Term Query
Bool Query
java
@Configuration
public class ElasticsearchConfig {
@Value("${app.elasticsearch.index-settings.number-of-shards:3}")
private int numberOfShards;
@Value("${app.elasticsearch.index-settings.number-of-replicas:1}")
private int numberOfReplicas;
@Bean
public ElasticsearchRestTemplate elasticsearchTemplate(ElasticsearchRestTemplate restTemplate) {
// 创建索引时应用配置
return restTemplate;
}
}
java
@Service
public class OptimizedSearchService {
public SearchHits<ArticleDocument> optimizedSearch(String keyword) {
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.multiMatchQuery(keyword, "title^2", "content")
.analyzer("ik_smart")
.fuzziness("AUTO"))
.withPageable(PageRequest.of(0, 20))
.withSourceFilter(new FetchSourceFilter(new String[]{"id", "title", "authorName"}, null))
.build();
query.setTrackTotalHits(false); // 对于大量数据,避免精确计数
return elasticsearchTemplate.search(query, ArticleDocument.class);
}
}
java
@Component
public class ElasticsearchHealthCheck {
private final ElasticsearchRestTemplate elasticsearchTemplate;
public ElasticsearchHealthCheck(ElasticsearchRestTemplate elasticsearchTemplate) {
this.elasticsearchTemplate = elasticsearchTemplate;
}
public boolean isClusterHealthy() {
try {
ClusterHealth clusterHealth = elasticsearchTemplate.execute(client ->
client.cluster().health(RequestOptions.DEFAULT));
return clusterHealth.getStatus() != ClusterHealthStatus.RED;
} catch (Exception e) {
return false;
}
}
}
java
@RestController
public class MetricsController {
private final MeterRegistry meterRegistry;
private final Counter searchRequestCounter;
public MetricsController(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.searchRequestCounter = Counter.builder("es.search.requests")
.description("Number of search requests")
.register(meterRegistry);
}
@PostMapping("/api/search")
public ResponseEntity<SearchResult> search(@RequestBody SearchRequest request) {
searchRequestCounter.increment();
// 搜索逻辑
}
}
通过本文的介绍,我们了解了如何在 Spring Boot + MySQL 的技术栈中集成 ElasticSearch,实现了:
这种架构模式既保留了关系型数据库的事务特性,又获得了搜索引擎的高性能查询能力,是构建现代 Web 应用的理想选择。
用 AI 大模型判断擦边:抖音直播狙击新型低俗诱导打赏,8.8 万人次被处罚
2025-11-14
宾利欧陆 Supersports 硬核回归:1940 年迄今最轻车型,657 马力后驱