ElasticSearch业务场景与面试题
以下是几个常见的 Elasticsearch 业务场景解决方案及面试题解析(含 Java 示例):
一、业务场景解决方案
场景 1:商品搜索与过滤
需求:电商平台需要支持多条件搜索(关键词、价格区间、分类、品牌)并按相关性排序。
解决方案:
- Bool Query:组合
must
(必须匹配)、should
(加分项)、filter
(无评分过滤)。 - Keyword 字段:分类/品牌等精确匹配字段使用
keyword
类型。 - 范围查询:价格区间使用
range
查询。 - 相关性排序:使用 TF-IDF 或 BM25 算法(ES 默认)。
// 使用 RestHighLevelClient(旧版)或 Java Client(新版)
SearchRequest request = new SearchRequest("products");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("name", "手机")) // 关键词匹配
.filter(QueryBuilders.rangeQuery("price").gte(1000).lte(5000)) // 价格过滤
.filter(QueryBuilders.termQuery("category", "electronics")) // 分类精确匹配
.should(QueryBuilders.matchQuery("brand", "Apple").boost(2.0f)); // 品牌加分
sourceBuilder.query(boolQuery)
.sort(SortBuilders.scoreSort()) // 按相关性排序
.from(0).size(10); // 分页
request.source(sourceBuilder);
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
场景 2:日志分析与聚合
需求:分析系统日志中不同错误级别的出现次数。
解决方案:
- Date Histogram:按时间区间聚合。
- Terms Aggregation:按错误级别分组统计。
- 索引设计:使用
@timestamp
字段记录日志时间。
SearchRequest request = new SearchRequest("logs");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 按错误级别聚合
TermsAggregationBuilder termsAgg = AggregationBuilders.terms("error_levels").field("level");
// 子聚合:按小时统计
DateHistogramAggregationBuilder dateHistogram = AggregationBuilders.dateHistogram("per_hour")
.field("@timestamp").calendarInterval(DateHistogramInterval.HOUR);
termsAgg.subAggregation(dateHistogram);
sourceBuilder.aggregation(termsAgg);
request.source(sourceBuilder);
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 解析聚合结果
Terms terms = response.getAggregations().get("error_levels");
for (Terms.Bucket bucket : terms.getBuckets()) {
String level = bucket.getKeyAsString();
DateHistogram dateHistogramAgg = bucket.getAggregations().get("per_hour");
for (DateHistogram.Bucket hourBucket : dateHistogramAgg.getBuckets()) {
System.out.println(level + " - " + hourBucket.getKeyAsString() + ": " + hourBucket.getDocCount());
}
}
场景 3:搜索自动补全
需求:实现类似 Google 的搜索框自动补全功能。
解决方案:
- Completion Suggester:专为自动补全设计的字段类型。
- N-gram 分词:支持部分匹配(如 “el” 匹配 “elasticsearch”)。
// 创建索引时定义补全字段
CreateIndexRequest request = new CreateIndexRequest("suggestions");
request.mapping(
"{\n" +
" \"properties\": {\n" +
" \"suggest\": {\n" +
" \"type\": \"completion\"\n" +
" }\n" +
" }\n" +
"}",
XContentType.JSON
);
client.indices().create(request, RequestOptions.DEFAULT);
// 查询时使用 Suggest
SuggestBuilder suggestBuilder = new SuggestBuilder()
.addSuggestion("my_suggestion",
new CompletionSuggestionBuilder("suggest").prefix("el").size(5));
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().suggest(suggestBuilder);
SearchRequest searchRequest = new SearchRequest("suggestions").source(sourceBuilder);
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 解析补全结果
CompletionSuggestion suggestion = response.getSuggest().getSuggestion("my_suggestion");
for (CompletionSuggestion.Entry.Option option : suggestion.getOptions()) {
System.out.println(option.getText().string());
}
二、高频面试题
问题 1:Elasticsearch 如何保证数据一致性?
- 答:ES 是近实时(NRT)系统,默认每秒刷新(refresh)一次。写入数据后需等待刷新或手动调用
refresh
才能搜索到。通过wait_for
参数可强制等待变更可见:IndexRequest request = new IndexRequest("index") .source("field", "value") .setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
问题 2:如何处理深度分页的性能问题?
- 答:避免使用
from + size
(如from=10000
),改用search_after
:SearchSourceBuilder sourceBuilder = new SearchSourceBuilder() .sort(SortBuilders.fieldSort("_id")) // 必须包含唯一排序字段 .size(10); // 第一次查询 SearchResponse response = client.search(request, RequestOptions.DEFAULT); SearchHit[] hits = response.getHits().getHits(); // 后续分页使用 search_after sourceBuilder.searchAfter(hits[hits.length - 1].getSortValues());
问题 3:如何同步 MySQL 数据到 Elasticsearch?
- 方案:
- Logstash JDBC Input:定时拉取 MySQL 数据。
- Binlog 监听:使用 Canal 或 Debezium 捕获 MySQL 变更,通过 Kafka 同步到 ES。
- 双写:应用层直接写入 MySQL 和 ES(需处理事务一致性)。
问题 4:如何优化 Elasticsearch 集群性能?
- 答:
- 硬件:使用 SSD、增加内存。
- 索引设计:合理设置分片数(建议单个分片 10-50GB)、关闭不需要的字段
norms
。 - 查询优化:避免通配符查询、使用
filter
替代query
避免评分。 - 冷热分离:将历史数据迁移到冷节点。
三、陷阱与注意事项
- 分片数不可变:创建索引时需预先规划分片数量。
- 脑裂问题:通过
discovery.zen.minimum_master_nodes
(旧版)或cluster.initial_master_nodes
(新版)配置避免。 - 映射爆炸:限制动态映射,避免过多字段导致内存问题。
以上内容覆盖了常见场景和面试考点,结合 Java 代码可快速实现功能验证。