# Мультизапросы

Мультизапросы, или пакетные запросы, позволяют отправлять несколько поисковых запросов в Manticore в рамках одного сетевого запроса.

👍 Зачем использовать мультизапросы?

Основная причина — производительность. Отправляя запросы в Manticore пакетом, а не по одному, вы экономите время за счет сокращения сетевых обходов. Кроме того, отправка запросов пакетом позволяет Manticore выполнять определенные внутренние оптимизации. Если оптимизации пакета неприменимы, запросы будут обрабатываться по отдельности.

⛔ Когда не следует использовать мультизапросы?

Мультизапросы требуют, чтобы все поисковые запросы в пакете были независимыми, что не всегда так. Иногда запрос B зависит от результатов запроса A, то есть запрос B можно настроить только после выполнения запроса A. Например, вы можете захотеть отобразить результаты из вторичного индекса только в том случае, если в основной таблице не найдено результатов, или вы можете указать смещение во втором наборе результатов на основе количества совпадений в первом наборе результатов. В этих случаях вам потребуется использовать отдельные запросы (или отдельные пакеты).

При использовании библиотек-коннекторов, таких как `mysqli` в PHP, вы можете добавить несколько запросов, а затем выполнить их все как единый пакет. Это будет работать как один пакет мультизапросов.

Примечание: Если вы используете консольный клиент MySQL, по умолчанию он интерпретирует точку с запятой (;) как разделитель и отправляет каждый запрос на сервер по отдельности; это не пакет мультизапросов. Чтобы переопределить это поведение, переопределите разделитель на стороне клиента на другой символ с помощью внутренней команды `delimiter`. После внесения этого изменения клиент будет отправлять всю строку с точками с запятой без изменений, что позволит работать "магии мультизапросов".

Такое побочное поведение консольного клиента часто может сбивать с толку, потому что вы можете заметить, что одна и та же последовательность команд ведет себя по-разному в консоли клиента MySQL по сравнению с другим протоколом, таким как SQL-over-HTTP. Это именно потому, что сам консольный клиент MySQL разделяет запросы с помощью точек с запятой, но другие протоколы могут отправлять всю последовательность как единый пакет.

<!--
data for the following example:

DROP TABLE IF EXISTS products;
CREATE TABLE products(title text, price float);
INSERT INTO products(title, price) VALUES
('remove hair cream', 20.50),
('remove hair gel', 18.00),
('hair brush', 5.25);
-->

<!-- example multi-query 1 -->
Вы можете выполнить несколько поисковых запросов с помощью SQL, разделив их точкой с запятой. Когда Manticore получает запрос в таком формате от клиента, будут применены все оптимизации между операторами.

Мультизапросы не поддерживают запросы с `FACET`. Количество мультизапросов в одном пакете не должно превышать [max_batch_queries](../Server_settings/Searchd.md#max_batch_queries).


<!-- intro -->
##### SQL:

<!-- request SQL -->

```sql
SELECT id, price FROM products WHERE MATCH('remove hair') ORDER BY price DESC; SELECT id, price FROM products WHERE MATCH('remove hair') ORDER BY price ASC
```
<!-- end -->

Из консольного клиента MySQL/MariaDB:
<!-- request SQL -->

```sql
DELIMITER _
SELECT id, price FROM products WHERE MATCH('remove hair') ORDER BY price DESC; SELECT id, price FROM products WHERE MATCH('remove hair') ORDER BY price ASC_
```

<!-- intro -->
##### JSON:

<!-- request JSON -->

```JSON
POST /sql?mode=raw -d "SELECT id, price FROM products WHERE MATCH('remove hair') ORDER BY price DESC; SELECT id, price FROM products WHERE MATCH('remove hair') ORDER BY price ASC"
```

<!-- end -->

## Оптимизации мультизапросов

Существует две основные оптимизации, о которых следует знать: оптимизация общих запросов и оптимизация общих поддеревьев.

**Оптимизация общих запросов** означает, что `searchd` определит все те запросы в пакете, где различаются только настройки сортировки и группировки, и *выполнит поиск только один раз*. Например, если пакет состоит из 3 запросов, все они для "ipod nano", но 1-й запрос запрашивает топ-10 результатов, отсортированных по цене, 2-й запрос группирует по ID поставщика и запрашивает топ-5 поставщиков, отсортированных по рейтингу, а 3-й запрос запрашивает максимальную цену, полнотекстовый поиск по "ipod nano" будет выполнен только один раз, и его результаты будут повторно использованы для построения 3 различных наборов результатов.

[Фасетный поиск](../Searching/Faceted_search.md) — это особенно важный случай, который выигрывает от этой оптимизации. Действительно, фасетный поиск может быть реализован путем выполнения нескольких запросов: один для получения самих результатов поиска, и несколько других с тем же полнотекстовым запросом, но с разными настройками группировки для получения всех требуемых групп результатов (топ-3 автора, топ-5 поставщиков и т.д.). Пока полнотекстовый запрос и настройки фильтрации остаются неизменными, сработает оптимизация общих запросов и значительно повысит производительность.

**Оптимизация общих поддеревьев** еще интереснее. Она позволяет `searchd` использовать сходства между пакетными полнотекстовыми запросами. Она определяет общие части полнотекстовых запросов (поддеревья) во всех запросах и кэширует их между запросами. Например, рассмотрим следующий пакет запросов:

```bash
donald trump president
donald trump barack obama john mccain
donald trump speech
```

Есть общая часть из двух слов `donald trump`, которую можно вычислить только один раз, затем закэшировать и использовать во всех запросах. И оптимизация общих поддеревьев делает именно это. Размер кэша на запрос строго контролируется директивами [subtree_docs_cache](../Server_settings/Searchd.md#subtree_docs_cache) и [subtree_hits_cache](../Server_settings/Searchd.md#subtree_hits_cache) (чтобы кэширование *всех* шестнадцати газиллионов документов, соответствующих "i am", не исчерпало оперативную память и мгновенно не убило ваш сервер).

<!-- example multi-query 2 -->
Как можно определить, были ли запросы в пакете фактически оптимизированы? Если да, то соответствующий журнал запросов будет содержать поле "multiplier", указывающее, сколько запросов было обработано вместе:

Обратите внимание на поле "x3". Оно означает, что этот запрос был оптимизирован и обработан в подпакете из 3 запросов.


<!-- intro -->
##### log:

<!-- request log -->
```bash
[Sun Jul 12 15:18:17.000 2009] 0.040 sec x3 [ext/0/rel 747541 (0,20)] [lj] the
[Sun Jul 12 15:18:17.000 2009] 0.040 sec x3 [ext/0/ext 747541 (0,20)] [lj] the
[Sun Jul 12 15:18:17.000 2009] 0.040 sec x3 [ext/0/ext 747541 (0,20)] [lj] the
```
<!-- end -->

<!-- example multi-query 3 -->
Для справки, вот как выглядел бы обычный журнал, если бы запросы не были пакетными:


<!-- intro -->
##### log:

<!-- request log -->
```bash
[Sun Jul 12 15:18:17.062 2009] 0.059 sec [ext/0/rel 747541 (0,20)] [lj] the
[Sun Jul 12 15:18:17.156 2009] 0.091 sec [ext/0/ext 747541 (0,20)] [lj] the
[Sun Jul 12 15:18:17.250 2009] 0.092 sec [ext/0/ext 747541 (0,20)] [lj] the
```
<!-- end -->

Обратите внимание, как время на запрос в случае мультизапроса улучшилось в 1,5–2,3 раза в зависимости от конкретного режима сортировки.

## Ограничения и поток выполнения мультизапросов

Мультизапросы в основном поддерживаются для пакетной обработки запросов и получения метаинформации после таких пакетов. Из-за этого ограничения в пакетах разрешено только небольшое подмножество операторов. В одном пакете можно комбинировать только операторы `SELECT`, `SHOW` и `SET`.

Вы можете использовать `SELECT` как обычно; однако обратите внимание, что все запросы будут выполняться вместе за один проход. Если запросы не связаны, нет пользы от мультизапросов. Демон обнаружит это и запустит запросы по одному.

Вы можете использовать `SHOW` для обработки *предупреждений*, *статуса*, *статуса агента*, *метаданных*, *профиля* и *плана*. Все остальные операторы `SHOW` в пакетах будут тихо игнорироваться без вывода. Например, вы не можете выполнить `SHOW TABLES`, `SHOW THREADS` или `SHOW VARIABLES`, или любой другой оператор, не упомянутый выше, при пакетной обработке.

Вы можете использовать `SET` только для `SET PROFILING`. Все остальные команды `SET ...` будут тихо игнорироваться.

Порядок выполнения также отличается. Демон обрабатывает пакеты в два прохода.

Сначала он собирает все операторы `SELECT` и выполняет все встреченные операторы `SET PROFILING` одновременно. Как побочный эффект, только последний оператор `SET PROFILING` является эффективным. Если вы выполните мультизапрос, например: `SET PROFILING=1; SELECT...; SHOW META; SHOW PROFILE; SET PROFILING=0`, вы не увидите никакого профиля, потому что на первом проходе демон выполняет `SET PROFILING=1`, а затем сразу `SET PROFILING=0`.

Во-вторых, демон пытается выполнить один пакетный запрос со всеми собранными операторами `SELECT`. Если операторы не связаны, он выполнит их один за другим.

Наконец, он проходит по исходной последовательности пакета и возвращает данные подрезультата и метаданные из набора результатов для каждого `SELECT` и `SHOW`. Поскольку все операторы `SET PROFILING` были выполнены на первом проходе, они пропускаются на этом втором проходе.
each `SELECT` and `SHOW`. Since all `SET PROFILING` statements were executed in the first pass, they are skipped on this second pass.