问题
深分页的问题是很清楚。Solr必须为返回的搜索结果准备一个列表,并返回它的一部分。如果该部分来源于该列表的前面并不难。但如果我们想返回第10000页(每页20条记录)的数据,Solr需要准备一个包含大小为200000(10000 * 20)的列表。这样,它不仅需要时间,还需要内存。
令人高兴的是,Solr 4.7的发布改变了这一状况,引入了游标的概念。游标是一个动态结构,不需要存储在服务器上。游标包含了查询的结果的偏移量,因此,Solr的不再需要每次从头开始遍历结果直到我们想要的记录,游标的功能可以大幅提升深翻页的性能。
用法
游标的使用非常简单。在第一个查询中,我们需要传递一个额外的参数- cursorMark = *,告诉Solr返回游标。在返回中除了搜索结果,我们还可以得到nextCursorMark信息。看看下面这个例子。
查询
我们从一个简单的查询开始:
|
这里我们传入一个cursorMark = *参数,告诉Solr的,我们要使用的光标。
搜索结果
上面的查询将返回以下搜索结果:
<?xml version="1.0" encoding="UTF-8"?> <response> <lst name="responseHeader"> <int name="status">0</int> <int name="QTime">33</int> <lst name="params"> <str name="sort">score desc,id asc</str> <str name="start">0</str> <str name="q">*:*</str> <str name="cursorMark">*</str> <str name="rows">1</str> </lst> </lst> <result name="response" numFound="32" start="0"> <doc> <str name="id">0579B002</str> <str name="name">Canon PIXMA MP500 All-In-One Photo Printer</str> <str name="manu">Canon Inc.</str> <str name="manu_id_s">canon</str> <arr name="cat"> <str>electronics</str> <str>multifunction printer</str> <str>printer</str> <str>scanner</str> <str>copier</str> </arr> <arr name="features"> <str>Multifunction ink-jet color photo printer</str> <str>Flatbed scanner, optical scan resolution of 1,200 x 2,400 dpi</str> <str>2.5" color LCD preview screen</str> <str>Duplex Copying</str> <str>Printing speed up to 29ppm black, 19ppm color</str> <str>Hi-Speed USB</str> <str>memory card: CompactFlash, Micro Drive, SmartMedia, Memory Stick, Memory Stick Pro, SD Card, and MultiMediaCard</str> </arr> <float name="weight">352.0</float> <float name="price">179.99</float> <str name="price_c">179.99,USD</str> <int name="popularity">6</int> <bool name="inStock">true</bool> <str name="store">45.19214,-93.89941</str> <long name="_version_">1461375031699308544</long></doc> </result> <str name="nextCursorMark">AoIIP4AAACgwNTc5QjAwMg==</str> </response> |
我们看到,除了平时返回的结果外,还多了一个游标数据nextCursorMark,使用这个值作为我们翻下一页的参数。
下一个查询
提交下面这个查询看一下:
|
结果如下:
<?xml version="1.0" encoding="UTF-8"?> <response> <lst name="responseHeader"> <int name="status">0</int> <int name="QTime">2</int> <lst name="params"> <str name="sort">score desc,id asc</str> <str name="indent">true</str> <str name="q">*:*</str> <str name="cursorMark">AoIIP4AAACgwNTc5QjAwMg==</str> <str name="rows">1</str> </lst> </lst> <result name="response" numFound="32" start="0"> <doc> <str name="id">100-435805</str> <str name="name">ATI Radeon X1900 XTX 512 MB PCIE Video Card</str> <str name="manu">ATI Technologies</str> <str name="manu_id_s">ati</str> <arr name="cat"> <str>electronics</str> <str>graphics card</str> </arr> <arr name="features"> <str>ATI RADEON X1900 GPU/VPU clocked at 650MHz</str> <str>512MB GDDR3 SDRAM clocked at 1.55GHz</str> <str>PCI Express x16</str> <str>dual DVI, HDTV, svideo, composite out</str> <str>OpenGL 2.0, DirectX 9.0</str> </arr> <float name="weight">48.0</float> <float name="price">649.99</float> <str name="price_c">649.99,USD</str> <int name="popularity">7</int> <bool name="inStock">false</bool> <date name="manufacturedate_dt">2006-02-13T00:00:00Z</date> <str name="store">40.7143,-74.006</str> <long name="_version_">1461375031846109184</long></doc> </result> <str name="nextCursorMark">AoIIP4AAACoxMDAtNDM1ODA1</str> </response> |
现在,返回的nextCursorMark变化了,这是新的游标。
进一步查询
接下来的查询就很清楚了,使用cursorMark参数不断翻页,再来一次:
|
总结
Solr的4.7引入的这个游标参数非常简单,大大提升了翻页的效果,详细的测试报告看这里:
http://searchhub.org/2013/12/12/coming-soon-to-solr-efficient-cursor-based-iteration-of-large-result-sets
java实现:
- static void deepPaging() throws SolrServerException{
- HttpSolrServer server = new HttpSolrServer("http://192.168.238.133:8080/solr/collection1");
- server.setSoTimeout(10000);
- server.setConnectionTimeout(10000);
- server.setDefaultMaxConnectionsPerHost(12);
- server.setAllowCompression(true);
- SolrQuery query = new SolrQuery();
- query.setQuery( "*:*" );
- query.setRows(4);
- query.addSort("price",ORDER.desc).addSort("id", ORDER.desc);
- query.set(CursorMarkParams.CURSOR_MARK_PARAM, "*");
- QueryResponse rsp = server.query( query );
- List<CursorMark> beans = rsp.getBeans(CursorMark.class);
- System.out.println(rsp.getNextCursorMark());//得到下一个游标
- for (CursorMark cursorMark : beans) {
- System.out.println(cursorMark);
- }
- }