Статьи

Глубокая проблема с пейджингом

Представьте себе следующую проблему — у нас есть приложение, которое ожидает, что Solr вернет результаты, отсортированные по некоторому полю. Эти результаты будут выгружены в GUI. Однако, если человек, использующий приложение с графическим интерфейсом, сразу выбирает десятую, двадцатую или пятидесятую страницу результатов поиска, возникает проблема — время ожидания. Мы можем что-нибудь сделать с этим? Да, мы можем немного помочь Solr.

Несколько цифр в начале

Начнем с запроса и статистики. Представьте, что у нас есть следующий запрос, который отправляется в Solr для получения пятисотой страницы результатов поиска:

q=*:*&sort=price+asc&rows=100&start=50000
 

Что Solr должен сделать Solr, чтобы получить и отобразить список результатов? Конечно, читайте документы из индекса Lucene. Есть, конечно, вопрос, сколько документов нужно прочитать из индекса? Это 100? К сожалению нет. Solr должен собрать 50.100 отсортированных документов из индекса Lucene, потому что нам нужно 100 документов, начиная с 50.000-го. Вроде страшно. Теперь давайте посмотрим на сравнение того, сколько времени требуется Solr для загрузки первой страницы результатов поиска (запрос q = *: * & sort = price + asc & row = 100 & start = 0) и сколько времени требуется для отображения последней страницы поиска. результаты (т. е. запрос q = *: * & sort = price + asc & lines = 100 & start = 999900). Тест проводился по индексу, содержащему миллион документов, состоящему из четырех полей: идентификатор (строка), имя (текст), описание (текст), цена (длинный).Перед каждой итерацией Solr запускался запрос, и Solr отключался. Эти шаги повторялись 100 раз для каждого из запросов, и время, указанное в таблице, является средним арифметическим времени выполнения запроса. Вот результаты теста:

запрос Среднее время отклика (мс)
д = *: * & рода = цена + по возрастанию и строки = 100 & = 0 начать 146
д = *: * & рода = цена + по возрастанию и строки = 100 & Start = 999900 3184

Типичные решения

Конечно, мы можем попытаться установить кэш или размер queryResultWindowSize , но возникнет проблема с тем, как установить размер, может возникнуть ситуация, когда будет недостаточно или не будет релевантной записи в памяти Solr и затем ожидание Время поиска n-й страницы будет очень долгим. Мы также можем попытаться добавить согревающие запросы, но мы не сможем подготовить все комбинации, но даже если бы мы могли, кеш должен быть большим. Поэтому мы не сможем достичь желаемых результатов ни с одним из этих решений.

Фильтры, фильтры и фильтр снова

This behavior Solr (and other applications based on Lucene too) is caused by the queue of documents, called. priority queue, which in order to display the last page of results must download all documents matching the query and return the ones we want located in the desired page. Of course, in our case, if we want the first page of search results queue will have 100 entries. However, if we want the last page will have Solr search in the queue to put one million documents. Of course what I told is in big simplification.

The idea is to limit the number of documents Lucene must put in the queue. How to do it ? We will use filters to help us, so in Solr we will use the fq parameter. Using a filter will limit the number of search results. The ideal size of the queue would be the one that is passed in the rows parameter of query. However, this situation is ideal and not very possible in most situations. An additional problem is that asking a query with a filter we can not determine the number of results, because we do not know how much documents will the filter return. The solution to this problem is the making two queries instead of just one – the first one to see how fimiting is our filter thus using rows=0 and start=0, and the second is already adequately calculated (example below).

The maximum price of the product in the test data is 10,000, and the minimum is 0. So to the first query we will add the following bracket: <0; 1000>, and to the second query, we add the following bracket: <9000; 10000>.

Disadvantages of solution based on filters

There is one minus the filter-based solution and it is quite large. It may happen that the number of results to which the filter is limiting the query is too small. What then? We should increase the choosen bracket for the filter. Of course we can calculate the optimal brackets on the basis of our data, but it depends on the data and queries and why I won’t be talking about this at this point.

What is the performance after the change?

So let’s repeat the tests, but now let’s implement the filter based approach. So the first will just return the first page of results (the query q=*:*&sort=price+asc&rows=100&start=0&fq=price:[0+TO+1000]). The second query (actually two queries) will will be used the check the number of results and then fetch those results (those two queries: q=*:*&sort=price+asc&rows=0&start=0&fq=price:[9000+TO+10,000] and q=*:*&sort=price+asc&rows=100&start=100037&fq=price:[9000+TO+10000]). It is worth noting about the changed start parameter in the query, due to the fact that we get fewer search results (this is caused by the fq parameter). This test was was carried out in similar way to the previous one – start Solr, run a query (or queries), and shutdown Solr. The number seen in the table are the arithmetic mean of query time execution.

Query Average response time (ms)
q=*:*&sort=price+asc&rows=100&start=0&fq=price:[0+TO+1000] 128
q=*:*&rows=0&fq=price:[9000+TO+10000]
q=*:*&sort=price+asc&rows=100&start=100037&fq=price:[9000+TO+10000]
422

As you can see, the query performance changed. We can therefore conclude that we succeeded. Of course, we could be tempted by further optimizations, but for now let’s say that we are satisfied with the results. I suspect however that you can ask the following question:

How is this handled by other search engines ?

Good question, but the answer is trivial in total – one of the methods is to prevent a very deep paging. This method shall include Google. Try to type the word “ask” for the search and go further than 91 page of search results. Google didn’t let me ;)

Conclusions

As you can see deep paging performance after our changes has increased significantly. We can now allow the user to search results without worrying that it will kill our Solr and the machine on which he works. Of course, this method is not without flaws, due to the fact that we need to know certain parameters (eg in our case, the maximum price for descending sort), but this is a solution that allows you to provide search results in a relatively low latency time when compared to the pre-optimization method.