用了solr一段时间后,我想了解下solr是如何包装lucene。因为使用过程中几乎没有遇到直接操作lucene代码的情况,这让我有些迷糊。今天来看看一个solr查询的执行过程,经过debug发现了以下堆栈。
再看堆栈顶的方法里的lucene工具类:
哈,这就是lucene类了。这个时候就可能产生很多疑问了,solr不是分core/collection么,这里怎么没有体现?solr不是有缓存么,这里怎么没有看到缓存?
为了回答这些问题,我们从头来梳理这个查询过程。
我的请求url是localhost:8080/solr_web/core/select?q=*:* 。这个url会匹配到上面的Filter。
这个call方法调用了一个init方法,这个方法从request取到了core的名称,并且获取这个core单独的handler:
3.执行调用链HttpSolrCall.execute->SolrCore.execute->SearchHandler.handleRequest/handleRequestBody。
这个方法中会遍历几种组件(SearchComponent)进行处理,本文涉及的是QueryComponent,进入它的process方法。
这里面根据参数会有对id,group by 等参数的特殊处理,我们先不关注这些。再进入SolrIndexSearcher.search/getDocListC方法。
缓存就是getDocListC用到的。如下:
这里有个filter的判断,这个filter我查了下并没有设置值的地方,所以正常的都为空。如果没有特殊配置应该都会进来判断一下缓存。
下面重点了解下缓存机制:
a.缓存的key是什么?
从上面代码上看是QueryResultKey对象,这不够具体。我们看看得到缓存的数据的过程,首先是queryResultCache.get(key),进入LRUCache的get方法就会发现这就是一个LinkedHashMap.get方法,那key的值实际上就是QueryResultKey.hashCode的返回值了。它是怎么计算的?从下图可以看出来,他就是全部搜索条件计算出来的一个hashcode。
b.缓存什么时候过期?
看一下LRUCache的init方法,会发现这个秘密。
它就是用了个LinkedHashMap,而LRU的并不是它实现的,是LinkedHashMap实现的。这里介绍一下LRU,它是Least recently used,最近最少使用算法。最近的意思是在队列前的,LinkedHashMap最先加入的元素。最少的意思最少被访问(get)的。下图这个图比形象:
而LinkedHashMap如何实现上面那个图的呢?1就不用说了,就是往队尾加。3也简单就是从队首移除。2是get方法做些处理,看LinkedHashMap源码:
如果accessOrder这个参数为true的时候,就会对链表作一个移动的操作,我就不贴afterNodeAccess的源码了。这正是LRUCache用的方法,见上面的图里的map = new LinkedHashMap(initialSize, 0.75f, true)这第三个参数正是accessOrder。
好了,缓存的分析结束了,知道了这些对我们设置缓存的大小的设置、理解缓存生效时间都有帮助吧。如果查询没有命中缓存,程序就会执行到本文第一张图的第一行方法,这个方法基本就是直接调用lucene的查询方法了。
最后来梳理下整个查询流程: