Elasticsearch架构
Elasticsearch架构
倒排索引原理
Elasticsearch通过倒排索引实现全文检索,将文档分词后建立词项到文档ID的映射。
正排索引: doc1 → "Elasticsearch是搜索引擎"
倒排索引: "搜索引擎" → [doc1, doc3, doc5]
"搜索" → [doc1, doc2, doc3, doc4, doc5]
@Service
public class ProductSearchService {
@Autowired
private ElasticsearchOperations operations;
public void indexProduct(Product product) {
IndexCoordinates index = IndexCoordinates.of("products");
operations.save(product, index);
}
public List<Product> search(String keyword) {
Query query = new NativeQueryBuilder()
.withQuery(QueryBuilders.multiMatchQuery(keyword, "name", "description")
.type(MultiMatchQueryBuilder.Type.BEST_FIELDS)
.fuzziness(Fuzziness.AUTO))
.withSort(Sort.by(Sort.Direction.DESC, "_score"))
.withPageable(PageRequest.of(0, 20))
.build();
return operations.search(query, Product.class).stream()
.map(SearchHit::getContent)
.collect(Collectors.toList());
}
}
分片策略
合理设置主分片和副本分片,平衡查询性能和存储容量。
PUT /products
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 2,
"refresh_interval": "30s"
},
"mappings": {
"properties": {
"name": { "type": "text", "analyzer": "ik_max_word" },
"price": { "type": "double" },
"category": { "type": "keyword" },
"created_at": { "type": "date" }
}
}
}
集群架构
ES集群由Master节点、Data节点和Coordinating节点组成。
# elasticsearch.yml
cluster.name: my-cluster
node.name: node-1
node.roles: [master, data]
path.data: /data/elasticsearch
network.host: 0.0.0.0
discovery.seed_hosts: ["node-1:9300", "node-2:9300", "node-3:9300"]
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]
@Configuration
public class ElasticsearchConfig {
@Bean
public ElasticsearchClient elasticsearchClient() {
RestClient restClient = RestClient.builder(
new HttpHost("localhost", 9200))
.setRequestConfigCallback(config -> config
.setConnectTimeout(5000)
.setSocketTimeout(60000))
.build();
ElasticsearchTransport transport = new RestClientTransport(
restClient, new JacksonJsonpMapper());
return new ElasticsearchClient(transport);
}
}
搜索优化
通过调整查询方式、使用filter缓存、优化mapping提升搜索性能。
public List<Product> optimizedSearch(String keyword, Double minPrice) {
Query query = new NativeQueryBuilder()
.withQuery(QueryBuilders.matchQuery("name", keyword))
.withFilter(QueryBuilders.rangeQuery("price").gte(minPrice))
.withTrackTotalHits(true)
.withSourceFilter(FetchSourceFilter.of().includes("name", "price"))
.withPageable(PageRequest.of(0, 20))
.build();
return operations.search(query, Product.class).getContent();
}