概要
Solr和ES搜索引擎都有集群模式,在Solr中可以向任何一个节点发起一个查询,如果查询中没有附带路由router键的话,该节点会扮演Merge的角色向集群中每个share的一个副本发起查询,最终将所有取到的结果排序,返回给客户端。
详细
为了测试需要在测试集群中构建一个Collection,有两个shard(shard中只有一个副本),使用查询Q为:id:9922600464a707520164a75ded18001f OR id:0000000064db34070164db344a460004,查询条件中的id在两个shard上各有一个存在。
从查询结果看集群中,一共累计进行了5次查询,为了实现一个全shard扫描查询将本来一个查询变成了5次查询,这还只是在shard为2的时候。经过试验,在shard为n的,集群merge查询会发起 1+n+1 到 1+2n 个子查询,所以在生产环境中尽量应该避免使用merge查询。
查询日志详细
最终结果集落在两个分片上
从2组上查询候选记录
15:09:31 [x:search4xxxx_shard2_replica1] INFO o.apache.solr.core.SolrCore.Request - [search4xxxx_shard2_replica1] webapp=/solr path=/select params={df=text &distrib=false &fl=id &fl=score &shards.purpose=4 &start=0 &fsv=true &shard.url=http://127.0.0.1:8080/solr/search4xxxx_shard2_replica1/ &rows=2 &version=2 &q=id:9922600464a707520164a75ded18001f+OR+id:0000000064db34070164db344a460004 &NOW=1533020971828 &isShard=true &wt=javabin&_=1533019917767} hits=1 status=0 QTime=0 |
从1组上查询候选记录
shards.purpose=4
15:09:31 [x:search4xxxx_shard1_replica1] INFO o.apache.solr.core.SolrCore.Request- [search4xxxx_shard1_replica1] webapp=/solr path=/select params={df=text&distrib=false&fl=id&fl=score&shards.purpose=4&start=0&fsv=true &shard.url=http://127.0.0.1:8080/solr/search4xxxx_shard1_replica1/ &rows=2 &version=2 &q=id:9922600464a707520164a75ded18001f+OR+id:0000000064db34070164db344a460004 &NOW=1533020971828 &isShard=true &wt=javabin &_=1533019917767} hits=1 status=0 QTime=0 |
从1组上召回field内容
shards.purpose=64
15:09:31 [x:search4xxxx_shard1_replica1] INFO o.apache.solr.core.SolrCore.Request- [search4xxxx_shard1_replica1] webapp=/solr path=/select params={ df=text &distrib=false &fl=id,[shard] &shards.purpose=64 &shard.url=http://127.0.0.1:8080/solr/search4xxxx_shard1_replica1/ &rows=2 &version=2 &q=id:9922600464a707520164a75ded18001f+OR+id:0000000064db34070164db344a460004 &NOW=1533020971828 &ids=0000000064db34070164db344a460004 &isShard=true &wt=javabin&_=1533019917767} status=0 QTime=0 |
从2组上召回field内容
shards.purpose=64
ids=9922600464a707520164a75ded18001f 15:09:31 [x:search4xxxx_shard2_replica1] INFO o.apache.solr.core.SolrCore.Request- [search4xxxx_shard2_replica1] webapp=/solr path=/select params={ df=text &distrib=false &fl=id,[shard] &shards.purpose=64 &shard.url=http://127.0.0.1:8080/solr/search4xxxx_shard2_replica1/ &rows=2 &version=2 &q=id:9922600464a707520164a75ded18001f+OR+id:0000000064db34070164db344a460004 &NOW=1533020971828 &ids=9922600464a707520164a75ded18001f &isShard=true &wt=javabin &_=1533019917767} status=0 QTime=0 |
总查询
15:09:31 [x:search4xxxx_shard2_replica1] INFO o.apache.solr.core.SolrCore.Request- [search4xxxx_shard2_replica1] webapp=/solr path=/select params={ q=id:9922600464a707520164a75ded18001f+OR+id:0000000064db34070164db344a460004 &indent=on &fl=id,[shard] &rows=2&wt=json &_=1533019917767} hits=2 status=0 QTime=44 |
利用Solr的SearchComponent来优化MergeQuery
Solr客户端查询经常有通过Id列表的方式获取数据(这些Id是分布在多个shard上),换句话说客户端不需要对查询结果翻页,结果都在一页之中,有这样的前提就意味着可以省掉数据集排序的过程了,而排序在Mergequery中占用了非常多的时间,通过这个优化可以大大提高MergeQuery的查询性能。
可以扩展org.apache.solr.handler.component.SearchComponent 类来实现:
覆写distributedProcess方法:
@Override public int distributedProcess(ResponseBuilder rb) throws IOException { if (rb.stage < ResponseBuilder.STAGE_GET_FIELDS) return ResponseBuilder.STAGE_GET_FIELDS; if (rb.stage == ResponseBuilder.STAGE_GET_FIELDS) { return createSubRequests(rb); } return ResponseBuilder.STAGE_DONE; }
将本来需要两个阶段的MergeQuery 简化成只有一个阶段(STAGE_GET_FIELDS阶段)省去了数据排序流程,直接出数据。详细代码:
package com.dfire.tis.solrextend.handler.component.s4product; import java.io.IOException; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.cloud.CloudDescriptor; import org.apache.solr.cloud.ZkController; import org.apache.solr.common.SolrException; import org.apache.solr.common.cloud.Slice; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.ShardParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.handler.component.ResponseBuilder; import org.apache.solr.handler.component.SearchComponent; import org.apache.solr.handler.component.ShardRequest; public class RealtimeGetQueryComponent extends SearchComponent { public static final String NAME = "RealtimeGetQuery"; @Override public void prepare(ResponseBuilder rb) throws IOException { } @Override public void process(ResponseBuilder rb) throws IOException { } @Override public int distributedProcess(ResponseBuilder rb) throws IOException { if (rb.stage < ResponseBuilder.STAGE_GET_FIELDS) return ResponseBuilder.STAGE_GET_FIELDS; if (rb.stage == ResponseBuilder.STAGE_GET_FIELDS) { return createSubRequests(rb); } return ResponseBuilder.STAGE_DONE; } public int createSubRequests(ResponseBuilder rb) throws IOException { SolrParams params = rb.req.getParams(); CloudDescriptor cloudDescriptor = rb.req.getCore().getCoreDescriptor().getCloudDescriptor(); ZkController zkController = rb.req.getCore().getCoreDescriptor().getCoreContainer().getZkController(); String collection = cloudDescriptor.getCollectionName(); for (Slice slice : zkController.getClusterState().getActiveSlices(cloudDescriptor.getCollectionName())) { String shard = slice.getName(); ShardRequest sreq = new ShardRequest(); sreq.purpose = 1; sreq.shards = sliceToShards(rb, collection, shard); sreq.actualShards = sreq.shards; SolrQuery squery = new SolrQuery(); squery.set(ShardParams.SHARDS_QT, "/select"); String fields = params.get(CommonParams.FL); if (fields != null) { squery.set(CommonParams.FL, fields); } squery.set("distrib", false); squery.setQuery(params.get(CommonParams.Q)); sreq.params = squery; sreq.params.set("distrib", false); rb.addRequest(this, sreq); } return ResponseBuilder.STAGE_DONE; } private String[] sliceToShards(ResponseBuilder rb, String collection, String slice) { String lookup = collection + '_' + slice; // seems either form may be filled in rb.slices? for (int i = 0; i < rb.slices.length; i++) { if (lookup.equals(rb.slices[i]) || slice.equals(rb.slices[i])) { return new String[] { rb.shards[i] }; } } throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Can't find shard '" + lookup + "'"); } @Override public String getDescription() { return NAME; } }
例子:
solrconfig:
<config> <searchComponent name="realtimeGetQueryComponent" class="handler.component.s4product.RealtimeGetQueryComponent" /> <requestHandler name="/rquery" class="solr.SearchHandler"> <lst name="defaults"> <str name="echoParams">explicit</str> <int name="rows">10</int> <str name="df">text</str> </lst> <arr name="last-components"> <str>realtimeGetQueryComponent</str> </arr> </requestHandler> </config>
查询代码:
public class TestRQuery extends BasicTestCase { public void testRquery() throws Exception { SolrQuery solrQuery = new SolrQuery(); StringBuilder idStr = new StringBuilder("is_valid:1 AND {!terms f=id}"); idStr.append( "000000006492817901649281d0380004,9992654455288a2b0155438eb12f00da,9992654461dbc2ad0161df5d152300fe,9992654355288a2b01553eaee9e400cb,9993236363d8ecba01641bf7d08c095f"); solrQuery.setQuery(idStr.toString()); solrQuery.setRows(5); solrQuery.setRequestHandler("/rquery"); System.out.println("start query"); QueryResponse response = client.mergeQuery("search4product", solrQuery, false); for (SolrDocument doc : response.getResults()) { System.out.println(doc.getFieldValue("id")); } } }
相关推荐
4. **查询与筛选**:Solr提供丰富的查询语法,包括标准查询解析器(Standard Query Parser)、Lucene查询语法(Lucene Query Parser)、布尔操作符、范围查询、高亮显示、分组和 faceting 等。这些功能使用户能精确...
- **Merge Policy**: 确定何时和如何合并索引段,影响性能和存储。 - **Index Writer**: 控制索引写入行为,如缓冲大小、刷新间隔等。 ### 3. WPSOLR与WordPress的集成 WPSOLR插件通过API与WordPress交互,将...
这本书的源代码不仅帮助读者理解Lucene的工作原理,还提供了实际操作的机会,以便于开发者能够熟练掌握搜索引擎的开发。 首先,我们要了解Lucene的核心概念。Lucene主要由以下几个部分组成: 1. 文档(Document)...
作为开源项目,其源码提供了丰富的学习资源,帮助开发者深入了解搜索引擎的工作原理和实现细节。在这个主题中,我们将对Lucene的源码进行深入的分析。 1. **Lucene的基本结构** - Lucene的核心组件包括索引(Index...
**Lucene 常用功能介绍** Lucene 是一个高性能、全文检索库,由Apache软件基金会开发并维护。...结合课程文档和视频讲解,可以更深入地理解Lucene的工作原理和使用技巧,从而更好地利用它来解决实际的搜索问题。
对于大规模数据,可以利用Solr或Elasticsearch这样的分布式搜索平台,它们基于Lucene构建,提供了集群部署、负载均衡和自动故障恢复等功能。 ### 八、优化与合并段 为了提高搜索性能,Lucene会定期进行段合并...
Lucene是一个库,而非完整的搜索引擎产品,因此通常与其他技术结合使用,如Solr(提供REST接口和集群功能)或Elasticsearch(分布式搜索引擎)。5.2.1版本可以轻松地与这些框架集成,实现更复杂的应用场景。 9. **...
1. 近实时搜索(Near Real-Time Search):Lucene引入了Segment和Merge策略,能够在添加新文档后几乎立即反映到搜索结果中。 2. 分布式搜索(Distributed Search):通过Solr或Elasticsearch等工具,Lucene可以扩展...
- **合并段(Merge Segments)**:当有多个段时,Lucene会定期进行段合并,以减少段的数量,优化磁盘空间利用率。 3. 全文搜索 - **查询分析(Query Analysis)**:用户输入的查询也会经过分析,确保与索引中的...
- **Query**: Lucene 支持多种查询类型,如 TermQuery(匹配单个词项)、BooleanQuery(组合多个查询)、PhraseQuery(匹配短语)等。查询对象表示了用户想要查找的信息模式。 - **Scoring**: Lucene 使用 TF-IDF...
7. **优化与备份**:索引优化(Merge)可以合并多个Segment,提高搜索性能。备份索引则确保数据安全。 【标签】:“lucene学习” 学习Lucene的同时,你可能会遇到以下标签相关的知识点: - **倒排索引**:Lucene...
9. **多线程与分布式搜索**:Lucene API设计允许在多线程环境中高效运行,并通过Solr或Elasticsearch等扩展实现分布式搜索,以处理大规模数据。 10. **内存与磁盘索引**:Lucene既支持内存中的临时索引,也支持持久...
下面,我们将深入探讨Lucene的核心概念、架构以及关键组件,通过分析其源码,来理解这个强大工具的工作原理。 1. **核心概念** - **倒排索引(Inverted Index)**:Lucene的主要数据结构,它将文档中的词项与文档...
通过Solr或Elasticsearch等工具,可以将一个大索引拆分成多个小索引(分片),然后在多台机器上并行处理,提高搜索性能和可扩展性。 八、优化与性能调优 为了保持最佳性能,需要定期对Lucene索引进行优化。优化过程...
- **合并段(Merge Segments)**: `IndexWriter`会在索引中创建多个段,定期合并小段可以提高搜索效率。 - **缓存(Cache)**: 使用`BitSet`缓存和`FilterCache`可以加快查询速度,特别是对于经常查询的过滤条件。...
- 分布式搜索:通过Solr或Elasticsearch在集群环境中扩展Lucene的能力。 - 高级查询构造:布尔查询、短语查询、范围查询、模糊查询等。 - 集成到Web应用:如Spring Boot中使用Lucene。 以上内容只是Lucene全文...
**标题:“Lucene建立索引”** **描述分析:** ...学习并实践“Lucene建立索引”,不仅可以深入了解倒排索引的工作原理,还能提升处理大规模文本数据的能力,为后续的全文搜索和数据分析打下坚实基础。
4. 集群和分布式搜索:如果数据量庞大,可能需要使用Solr或Elasticsearch这样的分布式搜索引擎,它们基于Lucene但提供了更高级的功能。 5. 性能调优:优化搜索速度、索引构建速度以及内存使用。 总的来说,Lucene是...
例如,使用Session对象的save()或persist()方法可实现对象的持久化,而get()和load()用于查询,update()和merge()用于更新,delete()则用于删除。 4. **级联操作**: 在关联映射中,Hibernate允许级联操作,这意味着...
5. **优化(Optimization)**:索引可能会随着不断添加和删除文档而变得碎片化,因此定期进行优化(Merge索引)是必要的,以提高搜索效率。 6. **更新和删除(Updating & Deleting)**:Lucene支持对已索引文档进行...