【架构实战】搜索引擎架构:ElasticSearch集群设计

张开发
2026/4/18 21:15:57 15 分钟阅读

分享文章

【架构实战】搜索引擎架构:ElasticSearch集群设计
一、ElasticSearch简介ElasticSearch是一个基于Lucene的分布式搜索和分析引擎核心特点全文搜索分布式实时分析高可用RESTful API近实时NRT索引典型使用场景全文检索日志分析ELK应用性能监控安全分析商品搜索二、集群架构设计1. 集群角色┌──────────────────────────────────────────────────────┐ │ Client Node │ │ 协调节点路由请求 │ └──────────────────────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Master Node │ │ Master Node │ │ Master Node │ │ 主节点 │ │ 候选节点 │ │ 候选节点 │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ └───────────────┼───────────────┘ ▼ ┌──────────────────────────────────────────────────────┐ │ Data Node数据节点 │ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ │ │Shard 0 │ │Shard 1 │ │Shard 2 │ │Shard 3 │ │ │ │(Primary)│ │(Primary)│ │(Replica)│ │(Replica)│ │ │ └────────┘ └────────┘ └────────┘ └────────┘ │ └──────────────────────────────────────────────────────┘2. 分片设计# elasticsearch.yml# 集群配置cluster.name:order-searchnode.name:node-1node.master:truenode.data:true# 最小主节点数防止脑裂discovery.zen.minimum_master_nodes:2# 分片配置index.number_of_shards:3# 主分片数index.number_of_replicas:1# 副本分片数# 内存配置bootstrap.memory_lock:true# 线程池配置thread_pool:search:size:20queue_size:10003. Docker集群部署# docker-compose.ymlversion:3services:es-master-1:image:elasticsearch:8.11.0container_name:es-master-1environment:-node.namemaster-1-cluster.nameorder-search-node.mastertrue-node.datafalse-discovery.seed_hostses-master-1,es-master-2,es-master-3-cluster.initial_master_nodesmaster-1,master-2,master-3ports:-9201:9200volumes:-es-master-1-data:/usr/share/elasticsearch/datamem_limit:1ges-master-2:image:elasticsearch:8.11.0container_name:es-master-2environment:-node.namemaster-2-cluster.nameorder-search-node.mastertrue-node.datafalsevolumes:-es-master-2-data:/usr/share/elasticsearch/dataes-data-1:image:elasticsearch:8.11.0container_name:es-data-1environment:-node.namedata-1-cluster.nameorder-search-node.masterfalse-node.datatruevolumes:-es-data-1-data:/usr/share/elasticsearch/dataes-data-2:image:elasticsearch:8.11.0container_name:es-data-2environment:-node.namedata-2-cluster.nameorder-search-node.masterfalse-node.datatruevolumes:-es-data-2-data:/usr/share/elasticsearch/datavolumes:es-master-1-data:es-master-2-data:es-data-1-data:es-data-2-data:三、索引设计1. Mapping设计PUT/products{settings:{number_of_shards:3,number_of_replicas:1,analysis:{analyzer:{ik_analyzer:{type:custom,tokenizer:ik_max_word,filter:[standard,lowercase]}}}},mappings:{properties:{product_id:{type:long},product_name:{type:text,analyzer:ik_analyzer,fields:{keyword:{type:keyword}}},description:{type:text,analyzer:ik_analyzer},category_id:{type:long},category_path:{type:keyword},brand_id:{type:long},brand_name:{type:keyword},price:{type:double},stock:{type:integer},sales_count:{type:integer},tags:{type:keyword},specs:{type:nested,properties:{name:{type:keyword},value:{type:keyword}}},images:{type:keyword},create_time:{type:date},update_time:{type:date}}}}2. 索引模板PUT/_index_template/product_template{index_patterns:[products-*],template:{settings:{number_of_shards:3,number_of_replicas:1,refresh_interval:5s},mappings:{properties:{product_id:{type:long},product_name:{type:text,analyzer:ik_max_word},price:{type:double},category_id:{type:long}}}}}3. 别名管理# 创建索引并指定别名PUT/products_2024_01{settings:{...},mappings:{...}}POST/_aliases{actions:[{add:{index:products_2024_01,alias:products}}]}# 切换别名零停机POST/_aliases{actions:[{remove:{index:products_2024_01,alias:products}},{add:{index:products_2024_02,alias:products}}]}四、Java客户端1. High-Level REST ClientdependencygroupIdco.elastic.clients/groupIdartifactIdelasticsearch-java/artifactIdversion8.11.0/version/dependencyConfigurationpublicclassElasticsearchConfig{BeanpublicElasticsearchClientclient(){RestClientrestClientRestClient.builder(newHttpHost(localhost,9200)).build();returnnewElasticsearchClient(newRestClientTransport(restClient,newJacksonJsonpMapper()));}}ServicepublicclassProductSearchService{AutowiredprivateElasticsearchClientclient;// 搜索商品publicSearchResultProductsearchProducts(SearchRequestrequest){try{SearchResponseProductresponseclient.search(s-s.index(products).query(q-q.bool(b-{// 关键词搜索if(StringUtils.hasText(request.getKeyword())){b.must(m-m.multiMatch(mm-mm.query(request.getKeyword()).fields(product_name^3,description,brand_name)));}// 分类过滤if(request.getCategoryId()!null){b.filter(f-f.term(t-t.field(category_id).value(request.getCategoryId())));}// 价格区间if(request.getMinPrice()!null||request.getMaxPrice()!null){b.filter(f-f.range(r-{varranger.field(price);if(request.getMinPrice()!null){range.gte(request.getMinPrice());}if(request.getMaxPrice()!null){range.lte(request.getMaxPrice());}returnrange;}));}returnb;})).from(request.getOffset()).size(request.getPageSize()).sort(so-{if(price.equals(request.getSortField())){returnso.field(f-{if(desc.equals(request.getSortOrder())){returnf.field(price).order(SortOrder.Desc);}returnf.field(price).order(SortOrder.Asc);});}returnso.field(f-f.field(_score).order(SortOrder.Desc));}).highlight(h-h.fields(product_name,f-f.preTags(em).postTags(/em))),Product.class);returnSearchResult.Productbuilder().total(response.hits().total().value()).items(response.hits().hits().stream().map(hit-{Productphit.source();p.setHighlight(hit.highlight().get(product_name));returnp;}).collect(Collectors.toList())).build();}catch(IOExceptione){thrownewRuntimeException(搜索失败,e);}}// 索引文档publicvoidindexProduct(Productproduct){try{client.index(i-i.index(products).id(product.getProductId().toString()).document(product));}catch(IOExceptione){thrownewRuntimeException(索引失败,e);}}// 批量索引publicvoidbulkIndexProducts(ListProductproducts){try{BulkRequest.BuilderbrnewBulkRequest.Builder();for(Productp:products){br.operations(op-op.index(idx-idx.index(products).id(p.getProductId().toString()).document(p)));}client.bulk(br.build());}catch(IOExceptione){thrownewRuntimeException(批量索引失败,e);}}}2. 拼音搜索支持PUT/products{settings:{analysis:{analyzer:{pinyin_analyzer:{tokenizer:pinyin_tokenizer,filter:[lowercase]}},tokenizer:{pinyin_tokenizer:{type:pinyin,lowercase:true,keep_first_letter:true,keep_full_pinyin:true,keep_none_chinese:true}}}},mappings:{properties:{product_name:{type:text,analyzer:ik_max_word,fields:{pinyin:{type:text,analyzer:pinyin_analyzer}}}}}}五、性能优化1. 查询优化// 避免深度分页使用Search AfterpublicSearchResultProductsearchAfter(SearchRequestrequest,StringsortValue){try{SearchResponseProductresponseclient.search(s-{varbuilders.index(products).query(q-q.matchAll(m-m)).size(20);if(sortValue!null){builder.searchAfter(searchAfter(sortValue));}returnbuilder;},Product.class);// 返回最后一条排序值用于下次查询returnSearchResult.Productbuilder().items(response.hits().hits()).lastSortValue(response.hits().hits().size()0?response.hits().hits().get(response.hits().hits().size()-1).sortValues()[0].toString():null).build();}catch(IOExceptione){thrownewRuntimeException(e);}}2. 写入优化# 写入优化配置index:number_of_shards:3number_of_replicas:0# 写入时设为0完成后设为1# 刷新间隔降低刷新频率提高写入性能refresh_interval:30s# 合并策略merge:scheduler.max_thread_count:1# 副本异步写入bootstrap.memory_lock:true3. 分片分配策略# 查看分片分配GET/_cat/shards?v # 手动移动分片POST/_cluster/reroute{commands:[{move:{index:products,shard:0,from_node:node-1,to_node:node-2}}]}# 设置副本数量PUT/products/_settings{number_of_replicas:1}六、监控与运维1. 集群健康# 查看集群健康状态GET /_cluster/health# 查看节点状态GET /_cat/nodes?v# 查看索引状态GET /_cat/indices?v2. 性能监控# 节点统计GET/_nodes/stats # 索引统计GET/products/_stats # 集群统计GET/_cluster/stats3. Curator管理# curator.ymlclient:hosts:-localhostport:9200actions:1:action:delete_indicesdescription:删除30天前的索引filters:-filtertype:agesource:creation_datedirection:olderunit:daysunit_count:30七、总结ElasticSearch是强大的分布式搜索引擎集群架构主节点 数据节点 协调节点索引设计合理分片 Mapping 别名查询优化避免深度分页 Search After写入优化降低副本 批量写入最佳实践根据数据量合理设置分片数做好冷热数据分离定期维护合并、清理完善的监控告警个人观点仅供参考

更多文章