Статьи

Solr 4.7 — эффективный глубокий пейджинг

Давным-давно мы описали проблему, называемую глубоким пейджингом . Короче говоря — чем глубже вы хотите углубиться в результаты, тем медленнее будет запрос. Это связано с тем, что Solr необходимо подготовить данные с начала для каждого запроса. До Solr 4.7 не было хорошего решения этой проблемы. С недавно выпущенной версией Solr у нас появилась возможность использовать так называемый курсор для существенного улучшения производительности глубокой подкачки.

Эта проблема

Глубокая проблема подкачки довольно легко определить. Чтобы вернуть результаты поиска, Solr должен подготовить структуру в памяти и вернуть ее часть. Возврат части структуры прост, если эта часть происходит из начала структуры. Однако, если мы хотим вернуть номер страницы 10.000 (где мы возвращаем 20 результатов на страницу), Solr должен подготовить структуру, содержащую минимум 200.000 элементов (10.000 * 20). Вы видите, что это требует не только времени, но и памяти.

Хорошо, что с выпуском Solr 4.7 ситуация изменилась — появился курсор. Курсор — это логическая структура, которая не требует сохранения своего состояния на стороне сервера. Курсор содержит информацию о сохранении и чтобы документ не возвращался в результатах. Из-за этого Solr не нужно начинать поиск с начала каждый раз, когда мы хотим получить следующую страницу результатов. Это приводит к значительному улучшению производительности при использовании курсора и углублении в результаты.

использование

Использование курсора очень просто. Чтобы сказать Solr вернуть курсор, в первом запросе нам нужно передать дополнительный параметр — cursorMark = * . В результате, кроме документов, мы получим идентификатор курсора, возвращенный в параметре nextCursorMark . Давайте посмотрим на пример.

Запрос

Давайте начнем с очень простого запроса:

1	curl 'localhost:8983/solr/select?q=*:*&rows=1&sort=score+desc,id+asc&cursorMark=*'

Здесь нас интересуют четыре вещи. Во-первых, мы либо опускаем параметр start, либо устанавливаем его в 0 . Параметр row может принимать нужные нам значения, ограничений на него нет. Конечно, мы передали параметр cursorMark = * , чтобы сообщить Solr, что мы хотим использовать курсор. Последнее, что мы сделали, это сортировка определения. Нам нужно определить сортировку для работы курсора, которая расскажет курсору, как себя вести. Вот почему нам нужно было перезаписать сортировку по умолчанию и включить сортировку не только по баллам, но и по идентификатору документа.

Результаты поиска

Наш запрос возвращает следующие результаты поиска:

01	<?xml version="1.0" encoding="UTF-8"?>
02	<response>
03	 <lst name="responseHeader">
04	  <int name="status">0</int>
05	  <int name="QTime">33</int>
06	  <lst name="params">
07	   <str name="sort">score desc,id asc</str>
08	   <str name="start">0</str>
09	   <str name="q">*:*</str>
10	   <str name="cursorMark">*</str>
11	   <str name="rows">1</str>
12	  </lst>
13	 </lst>
14	<result name="response" numFound="32" start="0">
15	 <doc>
16	  <str name="id">0579B002</str>
17	  <str name="name">Canon PIXMA MP500 All-In-One Photo Printer</str>
18	  <str name="manu">Canon Inc.</str>
19	  <str name="manu_id_s">canon</str>
20	  <arr name="cat">
21	   <str>electronics</str>
22	   <str>multifunction printer</str>
23	   <str>printer</str>
24	   <str>scanner</str>
25	   <str>copier</str>
26	  </arr>
27	  <arr name="features">
28	   <str>Multifunction ink-jet color photo printer</str>
29	   <str>Flatbed scanner, optical scan resolution of 1,200 x 2,400 dpi</str>
30	   <str>2.5" color LCD preview screen</str>
31	   <str>Duplex Copying</str>
32	   <str>Printing speed up to 29ppm black, 19ppm color</str>
33	   <str>Hi-Speed USB</str>
34	   <str>memory card: CompactFlash, Micro Drive, SmartMedia, Memory Stick, Memory Stick Pro, SD Card, and MultiMediaCard</str>
35	  </arr>
36	  <float name="weight">352.0</float>
37	  <float name="price">179.99</float>
38	  <str name="price_c">179.99,USD</str>
39	  <int name="popularity">6</int>
40	  <bool name="inStock">true</bool>
41	  <str name="store">45.19214,-93.89941</str>
42	  <long name="_version_">1461375031699308544</long></doc>
43	 </result>
44	 <str name="nextCursorMark">AoIIP4AAACgwNTc5QjAwMg==</str>
45	</response>

Как мы видим, в дополнение к стандартным результатам поиска мы получили идентификатор курсора в разделе nextCursorMark . Теперь, чтобы получить следующие результаты, связанные с этим курсором, нам нужно передать этот идентификатор с помощью параметра cursorMark .

Следующий запрос

Наш следующий запрос выглядит следующим образом (не значение параметра cursorMark ):

1	curl 'localhost:8983/solr/select?q=*:*&rows=1&sort=score+desc,id+asc&cursorMark=AoIIP4AAACgwNTc5QjAwMg=='

Результаты были следующими:

01	<?xml version="1.0" encoding="UTF-8"?>
02	<response>
03	 <lst name="responseHeader">
04	  <int name="status">0</int>
05	  <int name="QTime">2</int>
06	  <lst name="params">
07	   <str name="sort">score desc,id asc</str>
08	   <str name="indent">true</str>
09	   <str name="q">*:*</str>
10	   <str name="cursorMark">AoIIP4AAACgwNTc5QjAwMg==</str>
11	   <str name="rows">1</str>
12	  </lst>
13	 </lst>
14	<result name="response" numFound="32" start="0">
15	 <doc>
16	  <str name="id">100-435805</str>
17	  <str name="name">ATI Radeon X1900 XTX 512 MB PCIE Video Card</str>
18	  <str name="manu">ATI Technologies</str>
19	  <str name="manu_id_s">ati</str>
20	  <arr name="cat">
21	   <str>electronics</str>
22	   <str>graphics card</str>
23	  </arr>
24	  <arr name="features">
25	   <str>ATI RADEON X1900 GPU/VPU clocked at 650MHz</str>
26	   <str>512MB GDDR3 SDRAM clocked at 1.55GHz</str>
27	   <str>PCI Express x16</str>
28	   <str>dual DVI, HDTV, svideo, composite out</str>
29	   <str>OpenGL 2.0, DirectX 9.0</str>
30	  </arr>
31	  <float name="weight">48.0</float>
32	  <float name="price">649.99</float>
33	  <str name="price_c">649.99,USD</str>
34	  <int name="popularity">7</int>
35	  <bool name="inStock">false</bool>
36	  <date name="manufacturedate_dt">2006-02-13T00:00:00Z</date>
37	  <str name="store">40.7143,-74.006</str>
38	  <long name="_version_">1461375031846109184</long></doc>
39	 </result>
40	 <str name="nextCursorMark">AoIIP4AAACoxMDAtNDM1ODA1</str>
41	</response>

Как мы видим, возвращенный nextCursorMark снова стал другим.

Дальнейшие запросы

Логика для дальнейших запросов проста — мы используем параметр cursorMark со значением, возвращенным в предыдущих результатах поиска. Итак, еще раз, наш следующий запрос будет выглядеть следующим образом:

1	curl 'localhost:8983/solr/select?q=*:*&rows=1&sort=score+desc,id+asc&nextCursorMark=AoIIP4AAACoxMDAtNDM1ODA1'

Резюме

Простой API и огромный выигрыш в производительности в случае глубокого подкачки. Вот как я думаю, курсор, представленный в Solr 4.7, можно суммировать. Я решил не делать повторные тесты производительности, некоторые уже сделаны Крисом Хостеттером в его записи об этой функциональности. Если вы заинтересованы, пожалуйста, посмотрите: http://searchhub.org/2013/12/12/coming-soon-to-solr-efficient-cursor-based-iteration-of-large-result-sets/ .