设为首页 收藏本站
查看: 918|回复: 0

[经验分享] solr查询过程

[复制链接]

尚未签到

发表于 2018-11-2 06:02:58 | 显示全部楼层 |阅读模式
  原文:http://www.cnblogs.com/mandela/archive/2011/05/10/2041754.html
  Search步骤
  a.SolrParams参数准备
  q – 查询语句
  sort – 排序字段&排序方式
  rows – 返回多少条 start – 起始点
  fl – 返回字段,需要返回scorer字段的话这个KEY对应的VALUE里包含”score”就可以了
  qt – 查询类型,根据这个字段找Handler
  fq – FilterQuery
  b.根据coreName到coreContains拿到SolrCore,再到SolrParams里去qt的VALUE到SolrCore找对应的SolrRequestHandler
  如果没找到需要调用core.close表示归还,在从coreContains那里拿core时将它的被引用做了++操作了的
  c.根据SolrParams&SolrCore构造一个SolrQueryRequest,其包含如下属性SolrCore, SolrParams, Map context
  实例化一个SolrQueryResponse,有了这两个对象后就可以调用solrcore.execute执行查询操作了
  d.solrcore.execute – 实际上执行的是handler.handleRequest(request, response)
  [1].SolrRequestHandler可以配置三个不同重要性的默认参数,defaults,appends,invariants上面提到过,handler
  在处理请求的第一步就是用这三个SolrParams包装用户提交的SolrParams。其包装逻辑是params为用户提交的SolrParams
  defaults作为params的默认取值SolrParams -> params
  appends作为params里getParams添加参数的SolrParams –> params
  params作为invariants的默认取值SolrParams –> params
  [2].先前提到对于SearchHandler有SearchComponent,这里只需要注意下QueryComponent就可以了,添加其他的组件
  一 是可能没有用二是会带来内存开销,比如StatsComponent组件加入的话查询会返回符合结果的DocSet,其本身也没什么用在 SearchHandler处理查询请求逻辑里面其会先初始化一个ResponseBuilder,这个对象用来存放各个SearchComponent 处理的其他或者结构需要的数据,资源协调者;其再迭代SearchComponent调用prepare函数;其再迭代SearchComponent调 用process函数
  整个core里SearchComponent只有一个实例,处理查询请求的数据当然不能放在这些SearchComponent里,只能放在资源协调者ResponseBuilder上…
  [3].组件QueryComponent的prepare(ResponseBuilder)逻辑
  ---------
  request.params.get[fl]拿到希望返回的域,判断里面是否包含scorer –> 设置到ResponseBuilder上
  request.params.get[defType]拿到QueryParserPlugin名字,如果为null则使用LuceneQParserPlugin.NAME
  request.params.get[q]拿到查询字符串 –>设置到ResponseBuilder上
  QParser.getParser(queryStr, queryParserType, request)解析得到Qparser,其解析过程大致是
  ---------
  a.实例化一个Map用来存放从queryStr以正则表达式{!(.*)}抽取的group(1)里符合正则
  x|x=’(.*)’|x=”(.*)”|x=/$(.*)/$|x=(.*) |x=(.*)$ - x表示符合JAVA变量命名规则的字符串
  对于第一种往MAP里PUT(“type”, x),对于第四种则PUT(x, request.params.get(group(1)));其他的put(x, group(1))
  queryStr里正则{!(.*)}剩下的PUT(“v”, 剩下的str),MAP –> MapSolrParams作为localParams
  b.从localParams找”typd”.value作为指定QueryParserPlugin的值,如果没有还是使用默认的LuceneQParserPlugin.NAME
  如果queryStr里包含正则{!(.*)}则queryStr=localParams.get(“v”)
  c.根据指定的QueryParserPlugin的名称到core里获取QueryParserPlugin,调用其createParser函数构造Qparser
  其传入的参数有{queryStr, localParams, requst.params, request}
  d.以LuceneQParserPlugin为例子查看其构造Qparser - LuceneQParser的过程,Qparser的作用主要是构造一个Query
  在实例化时如果localParams.get(“tags”)不为null则会取request.context.get(“tags”)其值是一个
  Map,如果取的值是null则new以个PUT到request.context里,再来解析localParams.get(“tags”)
  得到的字符串,有”,”切割得到的子字符串PUT到request.context.get(“tags”)得到的Map中,VALUE=
  List.add(LuceneQParserPlugin),以解析{!tag=”wdx,monkey”}为例子,context.get(“tags”)里有两个映射
  “wdx” -> List – {LuceneQParserPlugin} //LuceneQParserPlugin持有request, queryStr, params, localParams
  “monkey” -> List – {LuceneQParserPlugin}
  ---------
  调用Qparser.parse获取Query的逻辑是
  a.到localParam&params里找”df” – 默认字段,找VALUE时localParam优先,找不到再到params里面,如果没找到则
  到core.indexSchema那里拿
  b.到localParam&params里找”q.op” – BooleanQuery默认关系,拿的逻辑同上
  c.实例化一个SolrQueryParser解析queryStr -> Query输出
  得到Query –>设置到ResponseBuilder上
  ---------
  抽取localParam&params里的”sort”,”rows”,”start” –> 设置到ResponseBuilder上,其中sort可以指定
  schema里指定的域名|score|docid
  同时把Qparser设置到ResponseBuilder上
  ---------
  处理”fq” – FilterQuery
  request.params.get(“fq”)得到过滤查询String[],迭代这个数组调用QParser.getParser(str, null, request)
  获取Qparser解析str得到过滤查询Query,QParser.getParser的逻辑上面介绍过,这样得到一个过滤查询的链表,将这个
  链表设置到ResponseBuilder上
  [4].组件QueryComponent的process(ResponseBuilder)逻辑
  a.每个request都只持有一个IndexSearcher的引用,通过调用core.getSearcher获取,QueryComponent
  需要先获取IndexSearcher,core.getSearcher里的逻辑是
  ----getSearcher的逻辑
  getSearcher – (forceNew, returnSearcher, waitSearcher-Futures)
  关注solr全局三个点调用getSearcher函数 : solrCore初始化时(false, false, null),QueryComponent处理查询
  请求时(false, true, null),UpdateHandler在处理commit请求时(true, false, new Future[1])
  ---------
  1.solrCore初始化时
  根据solrconfig配置的IndexReaderFactory&DirectoryFactory获取索引的IndexReader,再使用这个reader
  封装一个SolrIndexReader,再使用这个SolrIndexReader封装一个RefCounted(searcher的引用计数器,当搜索
  组件获取一个组件后引用++,用完后调用close引用--,当引用数为0时将这个引用从core管理的一个当前被使用的
  searcher的链表移除,同时调用searcher.close回收资源),将这个引用添加到core管理的一个当前被使用的searcher
  的链表里如果firstSearcherListeners不为空则回调这些监听器,这个回调是交给core的一个newSingleThreadExecutor去
  做的,再往这个线程池里添加一个任务:将这个RefCounted设置为core当前最新的searcher的引用计数器
  最后返回null,因为returnSearcher=false
  在solrCore初始化时这样做的主要目的是在初始化时就加载好IndexSearcher,搜索请求来了之后能立即返回,而不必等待加载IndexSearcher
  ---------
  2.QueryComponent处理查询请求时
  由于core当前最新的searcher的引用计数器不为null且这个获取IndexSearcher的请求不是强制要求获取最新的,且
  returnSearcher=true故直接返回core当前最新的searcher的引用计数器,且这个引用计数器做++
  这里面还有段当前searcher的引用计数器为null的逻辑,但是没有发现有什么情况会导致这种情况发生故不累述了
  ---------
  3.UpdateHandler在处理commit请求时
  首先到core管理的一个当前被使用的searcher的链表里获取目前最新的searcher;同时会加载索引目录下的
  index.properties文件(如果存在的话),拿到KEY=’index’的值,其指明目前索引的存放地方;如果获取的目录和当前
  最新的searcher使用的目录一致且solrConfig.reopenReaders为true则获取通过searher.reader.reopen获取
  最新的reader -> 封装成searcher,否则直接IndexReader.open获取reader。
  获取到searcher后的一段逻辑[RefCount封装,添加到searchers链表]和core初始化时是一样的,接下来的逻辑是
  如果solrConfig.useColdSearcher为TRUE其当前searcher的引用为null-导致来自QueryComponent的请求阻塞
  [现在还没发现什么情况会导致searcher的引用为null]
  立即将这个新的searcher的引用设置为core当前最新的searcher的引用计数器,这样来自QueryComponent的请求
  拿到这个引用后返回,当时这时这个新建的searcher是没有经过其前一个searcher的cache热身的,同时这样会导致这个
  新建的searcher不会进行热身活动
  如果solrConfig.useColdSearcher为FALSE则会往线程池里添加一个热身的任务
  如果newSearcherListeners不为空则回调这些监听器,也是给线程池的任务
  最后如果先前没有做将新的searcher的引用设置为core当前最新的searcher的引用计数器的行为的话,则往线程池添加
  一个任务 – 将新的searcher的引用设置为core当前最新的searcher的引用计数器
  最后返回null,因为returnSearcher=false
  ---------
  ----solr的索引视图
  用IndexReader构建SolrIndexSearcher时会先用solr的视图包装这个IndexReader –> SolrIndexReader
  solr 的索引视图在lucene上添加了一层快速处理多个索引目录的视图,在用IndexReader构建SolrIndexReader时会获取这个 reader下的所有叶子Reader-SegmentReader,并建立索引,基于这样索引传入一个DOCID后能快速的定位 SegementReader,即其会持有其下的所有SegementReader&其start-offset,同时search时如果是 TermQuery则直接用叶子reader来做
  ----SolrIndexSearcher.cache
  a.SolrIndexSearcher有四个缓存 – 对于cache来说有三个角色CacheConfig,SolrCache,CacheRegenerator
  其分工是CacheConfig来自于solrconfig文件配置,在一个searcher初始化时会迭代fieldValueCache,filterCache,
  queryResultCache,documentCache的配置,如果有配置则通过CacheConfig @class创建SolrCache实例
  CacheRegenerator是负责新searcher创建后用老的searcher热身行为的,会遍历老的searcher的各个cache的key-value
  具体做什么由各个CacheRegenerator说了算,因为这时候由于update的commit操作会有新的doc进入索引,所有老的value都不能用了,唯一能做的就是知道哪些key比较热门
  对于filterCache来说其会使用新的searcher去加载这些热门的key的值放到cache里
  对于queryResultCache来说其会使用新的searcher去加载这些热门query的值放到cache里,当时查询参数flag会加上
  NO_CHECK_QCACHE
  -------
  SolrCache : filterCache queryResultCache documentCache
  queryResultCache – key=QueryResultKey value=DocList,主要是存放一个查询请求返回的DocList数据,QueryResultKey计算hash的影响因素有 : query filters sort
  documentCache – key=int value=Document,主要是存放docid->document的映射,在加载document时都会走这个缓存,如果不存在则用reader去加载,然后PUT到cache里
  filterCache :key=Query value=DocSet,主要存放query->docSet的映射,满足这个query的所有docid的映射,其作用和
  queryResultCache比区别在于其存放的可能只是中间结果,queryResultCache存放的是最终结果
  值得注意的是使用这些cache是很耗内存空间的,为了限制内存空间的使用,cache的实现也是使用LRU机制,通过实现LinkedHashMap的removeEldestEntry函数里判断目前的大小是否大于了限制来实现的
  -------
  b.对于纯粹的来自QueryComponent组件的查询请求是只需要返回DocList就可以了,可能还需要分数,这里分析为了简便只考虑需要DocList和分数的情况
  根据offset+len计算当期期望的最大的docId,再和maxDoc比较,不能比maxDoc大
  如果queryResultCache存在且这个查询没有过滤查询则到用这个查询封装一个QueryResultKey到queryResultCache取值
  如果存在且[查询不需要分数或者cache里有分数]这样就立即拿到了docList返回,查询结束
  如果cache里没有找到或者需要分数而cache里没分数则需要走查询了,对于查询的结果如果可以cache则put到queryResultCache
  ---------------
  拿到DocList了后就可以给SolrQueryResponse返回了...


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-629497-1-1.html 上篇帖子: solr死锁问题升级版脚本 下篇帖子: lucene solr小知识点
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表