“深度分页”是Solr中效率低下的一个问题,随着页码数的加深,Solr越来越难返回查询结果。
这是因为,Solr是无状态的(Solr is basically stateless),用户若要对搜索结果进行分页,必须传递给Solr两个参数(rows、start)。举例来说:用户需要每页50条docs,Page#1的请求参数start=0&rows=50;Page#2的请求参数start=50&rows=50;Page#3的请求参数start=150&rows=50,以此类推。在第N批次时,为使Solr知道需返回哪一批次的50条docs,它需要构建一个内部队列(含有N+50个排序后的结果文档),然后丢弃到前N个docs,返回剩下的50个docs给用户。
这就意味着,随着分页结果的线性增长,Solr需要越多的内存来维护N+50个docs。简而言之,查询第N页的M条docs,Solr需要得到从第0到第NM个docs,但只展现M个docs给用户。
解决上述问题的办法是使用游标(Cursors)。游标是Solr中的逻辑概念。它不会包含任何server中的状态信息。当每一批次的最后一个文档的排序值返回给用户时,计算一个“mark”用来表示排序值的有序空间的逻辑点。同时该mark值会被指定下一批查询请求的参数,告知Solr下一次请求从哪开始。
使用Solr的游标,需要指定两个参数cursorMark和nextCursorMark
相关代码也较为简单,如下:
public void cursorMark() throws SolrServerException, IOException {
SolrQuery q = (new SolrQuery("*:*")).setRows(10).setSort(
SortClause.asc("id"));
String cursorMark = CursorMarkParams.CURSOR_MARK_START;
boolean done = false;
while (!done) {
q.set(CursorMarkParams.CURSOR_MARK_PARAM, cursorMark);
QueryResponse rsp = solrServer.query(q);
String nextCursorMark = rsp.getNextCursorMark();
doCustomProcessingOfResults(rsp);
if (cursorMark.equals(nextCursorMark)) {
done = true;
}
cursorMark = nextCursorMark;
}
}
参考资料:
[1]. jira-SOLR-5463
[2]. Lucidworks - Efficient Cursor Based Iteration of Large Result Sets