面试题答案
一键面试Elasticsearch 集群配置优化
- 节点配置
- 增加数据节点:如果集群资源允许,适当增加数据节点,以分摊数据存储和搜索负载。例如,在
elasticsearch.yml
配置文件中定义新的数据节点:
node.name: data - node - 3 node.data: true node.master: false
- 优化节点角色:明确区分主节点、数据节点和协调节点。主节点负责集群状态管理,数据节点负责数据存储和处理,协调节点负责接收和分发客户端请求。
# 主节点配置 node.name: master - node - 1 node.data: false node.master: true # 数据节点配置 node.name: data - node - 1 node.data: true node.master: false # 协调节点配置 node.name: coordinating - node - 1 node.data: false node.master: false
- 增加数据节点:如果集群资源允许,适当增加数据节点,以分摊数据存储和搜索负载。例如,在
- 索引配置
- 分片和副本:合理设置索引的分片数和副本数。对于高并发读场景,可以适当增加副本数;对于高并发写场景,要平衡分片数,避免过多分片导致性能下降。例如,创建索引时设置分片和副本:
var createIndexRequest = new CreateIndexRequest("my_index") { Settings = new IndexSettings { NumberOfShards = 5, NumberOfReplicas = 2 } }; var createIndexResponse = await elasticClient.Indices.CreateAsync(createIndexRequest);
- 索引模板:使用索引模板来统一索引配置,确保不同索引之间的一致性。例如:
{ "template": "my_index_*", "settings": { "number_of_shards": 3, "number_of_replicas": 1 } }
- 优化字段映射:根据业务需求,合理设置字段的数据类型,避免不必要的字段存储和索引。例如,对于不需要进行搜索的字段,可以设置
index: false
。
{ "properties": { "name": { "type": "text" }, "isDeleted": { "type": "boolean", "index": false } } }
C# 客户端优化
- 连接池优化
- 使用静态连接池:在高并发场景下,使用静态连接池可以减少频繁创建和销毁连接的开销。
var pool = new StaticConnectionPool(new[] { new Uri("http://localhost:9200"), new Uri("http://localhost:9201") }); var settings = new ConnectionSettings(pool); var elasticClient = new ElasticClient(settings);
- 设置连接超时和重试策略:合理设置连接超时时间,避免长时间等待。同时,配置重试策略,在连接失败或请求失败时进行重试。
settings.ConnectionTimeout(TimeSpan.FromSeconds(10)) .MaxRetries(3) .RetryOnTimeout(true);
- 缓存优化
- 搜索结果缓存:对于一些不经常变化的数据,可以使用本地缓存(如
MemoryCache
)来缓存搜索结果。
private static MemoryCache _cache = new MemoryCache(new MemoryCacheOptions()); public async Task<ISearchResponse<MyDocument>> SearchAsync(string query) { if (_cache.TryGetValue(query, out object cachedResult)) { return (ISearchResponse<MyDocument>)cachedResult; } var searchRequest = new SearchRequest<MyDocument> { Query = new QueryStringQuery { Query = query } }; var response = await elasticClient.SearchAsync<MyDocument>(searchRequest); _cache.Set(query, response, DateTimeOffset.Now.AddMinutes(5)); return response; }
- 搜索结果缓存:对于一些不经常变化的数据,可以使用本地缓存(如
搜索请求处理优化
- 批量请求
- 使用 MultiSearch:如果有多个搜索请求,可以使用
MultiSearch
合并为一个请求,减少网络开销。
var multiSearchRequest = new MultiSearchRequest(); var searchRequest1 = new SearchRequest<MyDocument> { Query = new QueryStringQuery { Query = "query1" } }; var searchRequest2 = new SearchRequest<MyDocument> { Query = new QueryStringQuery { Query = "query2" } }; multiSearchRequest.Operations.Add(searchRequest1); multiSearchRequest.Operations.Add(searchRequest2); var multiSearchResponse = await elasticClient.MultiSearchAsync(multiSearchRequest);
- 使用 MultiSearch:如果有多个搜索请求,可以使用
- 优化查询语句
- 避免通配符查询:通配符查询(如
*query*
)性能较差,尽量使用更精准的查询,如QueryStringQuery
或TermQuery
。 - 使用过滤器:将过滤条件从查询中分离出来,使用过滤器可以利用 Elasticsearch 的缓存机制,提高查询性能。
var searchRequest = new SearchRequest<MyDocument> { Query = new BoolQuery { Filter = new TermQuery { Field = "category", Value = "electronics" } } };
- 避免通配符查询:通配符查询(如
- 分页优化
- 使用 Scroll API:对于大量数据的分页,使用
Scroll API
而不是from
和size
参数。Scroll API
适合一次性获取大量数据,而from
和size
在数据量较大时性能会急剧下降。
var searchRequest = new SearchRequest<MyDocument> { Scroll = TimeSpan.FromMinutes(5), Size = 100 }; var searchResponse = await elasticClient.SearchAsync<MyDocument>(searchRequest); var scrollId = searchResponse.ScrollId; while (true) { var scrollResponse = await elasticClient.ScrollAsync<MyDocument>(TimeSpan.FromMinutes(5), scrollId); if (!scrollResponse.Documents.Any()) { break; } // 处理文档 scrollId = scrollResponse.ScrollId; } // 清除滚动上下文 var clearScrollResponse = await elasticClient.ClearScrollAsync(new ClearScrollRequest(scrollId));
- 使用 Scroll API:对于大量数据的分页,使用