# Проблема с быстродействием при запросе c "fuzziness"

**URL:** https://discuss.elastic.co/t/c-fuzziness/59692
**Category:** Вопросы на русском языке
**Created:** [September 2, 2016, 5:20pm UTC](https://discuss.elastic.co/t/c-fuzziness/59692 "2016-09-02T17:20:08Z")
**Posts on this page:** 15
**Page:** 1

<div class="post-metadata">

### Author: ![Mike\_Patutin](https://avatars.discourse-cdn.com/v4/letter/m/a698b9/32.png) [@Mike\_Patutin](https://discuss.elastic.co/u/Mike_Patutin)
#### Post date: [September 2, 2016, 5:20pm UTC](https://discuss.elastic.co/t/c-fuzziness/59692/1 "2016-09-02T17:20:08Z")

</div>

День добрый.

Исторически сложилось, что есть 18 одинаковых индексов. В каждом хранятся статьи на своем языке.  
18 индексов == 18 В каждом индексе хранятся свои документы.

```
  "mappings": {
    "article": {"_meta": {"model": "WWW\SearchBundle\Document\Article" },
      "_all": {"auto_boost": true },
      "properties": {
        "keywords": {"index": "not_analyzed","type": "string"},
        "description": {"type": "string",
          "fields": {
            "language": {"type": "string"}
          }
        },
        "title": {"boost": 10,"type": "string",
          "fields": {
            "total": {"index": "not_analyzed", "type": "string"},
            "language": {"type": "string"}
          }
        }
      }
    }

```

Запросы все одинаковые, это типа поисковика:

```
 {
  "query": {
    "filtered": {
      "query": {
        "dis_max": {
          "queries": [
            {
              "constant_score": {
                "query": {
                  "multi_match": {
                    "type": "phrase",
                    "query": "donald trump us election",
                    "fields": [
                      "title"
                    ],
                    "fuzziness": 0,
                    "slop": 2
                  }
                },
                "boost": 5
              }
            },
            {
              "constant_score": {
                "query": {
                  "multi_match": {
                    "type": "phrase",
                    "query": "donald trump us election",
                    "fields": [
                      "description^5",
                      "keywords^0.25"
                    ],
                    "fuzziness": 0,
                    "slop": 2
                  }
                },
                "boost": 2
              }
            },
            {
              "constant_score": {
                "query": {
                  "multi_match": {
                    "type": "best_fields",
                    "query": "donald trump us election",
                    "fields": [
                      "title^2",
                      "description^0.2",
                      "keywords^0.25"
                    ],
                    "fuzziness": 1
                  }
                },
                "boost": 0.001
              }
            }
          ]
        }
      }
    }
  },
  "rescore": [
    {
      "query": {
        "rescore_query": {
          "function_score": {
            "functions": [
              {
                "gauss": {
                  "publishedAt": {
                    "origin": "2016-09-01T10:30:55-04:00",
                    "scale": "365d",
                    "decay": 0.5
                  }
                },
                "weight": 1
              }
            ]
          }
        },
        "rescore_query_weight": 1,
        "query_weight": 1,
        "score_mode": "multiply"
      },
      "window_size": 100000
    }
  ],
}

```

проблемы присутствуют в двух местах:

1. функция rescore. Если запрос популярный, то могут возвращаться порядка 1 000 000 результатов, которые нужно обработать.
2. Третий подзапрос с "fuzziness": 1

Включение и отключение "fuzziness": 1 или 0 вызывает броски в быстродействии в 2 раза. При включении нечеткого поиска запрос выполняется примерно 400мс. при отключении - 200мс

замена rescore на сортировку тоже увеличивает быстродействие в 2 раза. для популярных выражений. Для редких статей - влияния не оказывает естественно.

**Задача, ускорить запросы раз в 10. Ну или хотябы чтобы меньше 100мс уйти.**  
Думал сделать фильтрацию результатов, однако не могу, потому что подзапросы возвращают несортированные данные и если резать результаты до 10000, то получается совсем грустно. а если больше - то особого смысла нет. все равно тупит.

Посоветуйте пожалуйста, как мне можно ограничить количественно выдачу из основного запроса, при этом получить наиболее поздние документы, попадающие в выборку, и удовлетворяющие подзапросам.

В данный момент по совокупности в индексах примерно 5 000 000 документов. Самый большой индекс русский (1 200 000 документов ).

---

<div class="post-metadata">

### Author: ![Igor\_Motov](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/igor_motov/32/45193_2.png) [@Igor\_Motov](https://discuss.elastic.co/u/Igor_Motov)
#### Post date: [September 5, 2016, 4:17pm UTC](https://discuss.elastic.co/t/c-fuzziness/59692/2 "2016-09-05T16:17:26Z")

</div>

Какая версия и как время запроса изменяется, если временно изменить gauss на linear?

---

<div class="post-metadata">

### Author: ![Mike\_Patutin](https://avatars.discourse-cdn.com/v4/letter/m/a698b9/32.png) [@Mike\_Patutin](https://discuss.elastic.co/u/Mike_Patutin)
#### Post date: [September 5, 2016, 4:48pm UTC](https://discuss.elastic.co/t/c-fuzziness/59692/3 "2016-09-05T16:48:44Z")

</div>

Версия 1.7.3  
Делаю запрос по "donald trump us election"  
"total": 1242827,  
для gauss - скорость запроса 511ms  
для linear - 450ms

заменяю блок "rescope" на

```
  "sort": [
    "_score",
    {
      "publishedAt": {
        "order": "desc",
        "ignore_unmapped": true
      }
    }
  ],

```

получается 270ms

---

<div class="post-metadata">

### Author: ![Igor\_Motov](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/igor_motov/32/45193_2.png) [@Igor\_Motov](https://discuss.elastic.co/u/Igor_Motov)
#### Post date: [September 7, 2016, 2:18pm UTC](https://discuss.elastic.co/t/c-fuzziness/59692/4 "2016-09-07T14:18:49Z")

</div>

Еще пара вопросов. А что получается если `function_score` из `rescore` в основной запрос переместить?

```auto
{
  "query": {
    "function_score": {
      "query": {
        "filtered": {
          "query": {
            "dis_max": {
             ...
            }
          }
        }
      },
      "functions": [
        {
          "gauss": {
            "publishedAt": {
              "origin": "2016-09-01T10:30:55-04:00",
              "scale": "365d",
              "decay": 0.5
            }
          },
          "weight": 1
        }
      ],
      "boost_mode": "multiply"
    }
  }
}

```

Тоже самое с `linear` вместо `gauss`?

Какие диски используются, сколько физической памяти на машинах и какой размер heap-a?

---

<div class="post-metadata">

### Author: ![Mike\_Patutin](https://avatars.discourse-cdn.com/v4/letter/m/a698b9/32.png) [@Mike\_Patutin](https://discuss.elastic.co/u/Mike_Patutin)
#### Post date: [September 7, 2016, 4:21pm UTC](https://discuss.elastic.co/t/c-fuzziness/59692/5 "2016-09-07T16:21:15Z")

</div>

Игорь, огромное спасибо за ваши отклики.

При переносе в основной запрос, скорость увеличивается для  
gauss в основном запросе получается примерно 280мс, для linear 250мс

на машине 32Gb памяти, хип менял от 2 до 31Gb, сейчас стоит 16Gb  
Если смотреть в top, то больше 2,5 гигабайт памяти не отъедается вообще.

Диски  
ATA-9: INTEL SSDSC2BB480H4, D2010380, max UDMA/133  
две штуки в зеркале.

Но активность на дисковой подсистемы равна нулю после первых 2-3 запросов.

Процессор  
Intel(R) Xeon(R) CPU E5-1630 v3 @ 3.70GHz (fam: 06, model: 3f, stepping: 02)

Визуально, когда делаешь даже один запрос, нагрузка на процессоре в топе на секунду-другую прыгает до 40-70 процентов.  
Если просто из скрипта начать курлом дергать последовательно один и тот же запрос то диски стоят, на процессоре 300% нагрузки, load average = 4.

---

<div class="post-metadata">

### Author: ![Igor\_Motov](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/igor_motov/32/45193_2.png) [@Igor\_Motov](https://discuss.elastic.co/u/Igor_Motov)
#### Post date: [September 7, 2016, 5:19pm UTC](https://discuss.elastic.co/t/c-fuzziness/59692/6 "2016-09-07T17:19:15Z")

</div>

Если скорость улучшается, то лучше rescore не пользоваться - так как это дополнительный запрос и только по окну. Так что толку от него никакого - один вред в данном случае.

Если elasticsearch heap не использует, то имеет смысл у него его отнять и отдать OS под файловый кэш.

Давайте вот что еще попробуем. Запустите, опять ваш скрипт, желательно на нескольких потоках чтобы на ноде постоянно что-то искалось, и пока он работает, выполните вот эту команду 10 раз с интервалом в 2 сек и выложите куда-нибудь результат

```auto
curl "localhost:9200/_nodes/hot_threads?threads=100"    

```

---

<div class="post-metadata">

### Author: ![Mike\_Patutin](https://avatars.discourse-cdn.com/v4/letter/m/a698b9/32.png) [@Mike\_Patutin](https://discuss.elastic.co/u/Mike_Patutin)
#### Post date: [September 7, 2016, 7:16pm UTC](https://discuss.elastic.co/t/c-fuzziness/59692/7 "2016-09-07T19:16:58Z")

</div>

Ok.  
Вот что еще заметил. После переноса  
Сначала запустил оригинальный запросв function\_score в основной запрос, явно ухудшилась релевантность, выросла скорость, но увеличилась нагрузка на процессор. примерно в 2 раза. Как я понимаю, в этом случае у эластика получается параллелить расчет функции, за счет этого и получается ускорение.  
Решил провести эксперимент, заодно и получить статистику по тредам.  
Сначал запускался оригинальный запрос с rescore.  
10 потоков оп 10000 запросов в каждом потоке.  
LA=15, загрузка проца 780% (на восьми ядрах) по ядрам 97% user, 1% на system, 0% wait  
данные по тредам: [https://drive.google.com/file/d/0B30qqGOh6OSuQUk3YjNKUEFiR2s/view?usp=sharing](https://drive.google.com/file/d/0B30qqGOh6OSuQUk3YjNKUEFiR2s/view?usp=sharing)

Потом запускался запрос с перенесенной function\_score  
теже 10 потоков, циклы по 10000 запросов  
Структура нагрузки практически идентичная, тольк user 95% system 3%  
данные по тредам [https://drive.google.com/file/d/0B30qqGOh6OSueVFJQVdFbW9vWk0/view?usp=sharing](https://drive.google.com/file/d/0B30qqGOh6OSueVFJQVdFbW9vWk0/view?usp=sharing)

Не берусь судить, но сдается мне проблема в том, что подзапросы возвращают очень много статей, и по всем по ним, нужно расчитать новый вес

Вот вопрос, а нельзя ли ограничить каким либо образом количество выдаваемых статей одним подзапросом, чтобы они возвращали не 1 200 000 как сейчас по популярным запросам, а скажем каждый ограничивался 1000 самых релевантных?

---

<div class="post-metadata">

### Author: ![Igor\_Motov](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/igor_motov/32/45193_2.png) [@Igor\_Motov](https://discuss.elastic.co/u/Igor_Motov)
#### Post date: [September 10, 2016, 3:25am UTC](https://discuss.elastic.co/t/c-fuzziness/59692/8 "2016-09-10T03:25:52Z")

</div>

Ничего в глаза в hot\_threads не бросается, к сожалению.

Вы сказали, что у вас 18 индесков, а сколько шард в каждом индексе?

> Вот вопрос, а нельзя ли ограничить каким либо образом количество выдаваемых статей одним подзапросом, чтобы они возвращали не 1 200 000 как сейчас по популярным запросам, а скажем каждый ограничивался 1000 самых релевантных?

Не уверен, что я правильно понимаю что вы хотите этим добиться, но можно попробовать разбить основной запрос с помощью msearch, и тогда сортировка будет происходить по каждому подзапросу независимо.

А вы тоже самое под 2.x не пробовали?

---

<div class="post-metadata">

### Author: ![Mike\_Patutin](https://avatars.discourse-cdn.com/v4/letter/m/a698b9/32.png) [@Mike\_Patutin](https://discuss.elastic.co/u/Mike_Patutin)
#### Post date: [September 10, 2016, 4:44am UTC](https://discuss.elastic.co/t/c-fuzziness/59692/9 "2016-09-10T04:44:08Z")

</div>

В каждом индексе по 5 шард.

Попробую объяснить в терминах SQL:  
Пусть есть таблица

```
CREATE TABLE large_table (
  id BIGINT PRIMARY KEY,
  doc_id BIGINT,
  word_field VARCHAR(255),
  relevance int
);
create INDEX wf_idx ON large_table(word_field);

```

Таблица реально большая, скажем миллиардов 15 записей и поле word\_field может повторяться многократно. Предположим, что мы берем с нескольких больших новостных ресурсов статьи, делим их на слова, и кортеж идентификатор, ид документа, слово, и некий вес, закидываем в эту таблицу.  
Специально утрирую только для демонстрации.  
После чего делаем запрос по самым популярным словам "и", "в", "не"

```
SELECT * FROM (
  SELECT id,word_field,relevance FROM large_table WHERE word_field = 'и'
     UNION
  SELECT id,word_field,relevance FROM large_table WHERE word_field = 'в'
     UNION
  SELECT id,word_field,relevance FROM large_table WHERE word_field = 'не'
) tmp_table
ORDER BY relevance DESC
LIMIT 20

```

выполняться он будет просто ну ОЧЕНЬ долго. поскольку каждый из подзапросов вернет нам по паре миллионов строк, и все это счастье нужно пропустить через UNION, отсортировать, и выбрять первые.  
Если же в основном запросе будут вычислимые поля, или вместо SORT будет какой нибудь OUTER APPLY то это дело вообще превращается в жуткие пипец.

Решается просто, хотя запрос вроде бы сложнее:

```
SELECT * FROM (
                SELECT * FROM (SELECT id,word_field,relevance FROM large_table WHERE word_field = 'и' ORDER BY relevance DESC LIMIT 1000)
                UNION
                SELECT * FROM (SELECT id,word_field,relevance FROM large_table WHERE word_field = 'в' ORDER BY relevance DESC LIMIT 1000)
                UNION
                SELECT * FROM (SELECT id,word_field,relevance FROM large_table WHERE word_field = 'не' ORDER BY relevance DESC LIMIT 1000)
              ) tmp_table
ORDER BY relevance
LIMIT 20

```

Этот запрос отработает в сотни раз быстрее, и с ВЫСОКОЙ степенью вероятности вернем мне такой же результат.

Мне кажется, что моя проблема, сродни тому, что я только что описал.  
У меня тормоза на популярных запросах, когда каждый из запросов внутри dis\_max возвращает огромное количество документов, к которым эластик должен применить рескор (или функцию как вы предлагали) с достаточно тяжелыми расчетами.  
А вот если бы каждый из подзапросов ограничить выдачей 1000 наиболее релевантных статей, все стало бы сильно веселей.  
По непопулярным словам то у меня поиск идет очень быстро.

---

<div class="post-metadata">

### Author: ![Mike\_Patutin](https://avatars.discourse-cdn.com/v4/letter/m/a698b9/32.png) [@Mike\_Patutin](https://discuss.elastic.co/u/Mike_Patutin)
#### Post date: [September 10, 2016, 4:47am UTC](https://discuss.elastic.co/t/c-fuzziness/59692/10 "2016-09-10T04:47:31Z")

</div>

На 2.x не пробовал. Затруднительно по организационным причинам.

---

<div class="post-metadata">

### Author: ![Igor\_Motov](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/igor_motov/32/45193_2.png) [@Igor\_Motov](https://discuss.elastic.co/u/Igor_Motov)
#### Post date: [September 12, 2016, 12:23am UTC](https://discuss.elastic.co/t/c-fuzziness/59692/11 "2016-09-12T00:23:48Z")

</div>

Все понял, но только в elasticsearch процесс выполнения запроса выглядит совсем по другому. Дело в том, что в нем нет никакого UNION. И запрос по нескольким полям выполняются за один проход. То есть, если утрировать этот процесс, то получится что-то вроде следующего. Для каждого слова хранится битовая карта, в которой помечены все документы, которые это слово содержат:

```auto
   0123456789........
и 0000101110........
в 1100001100........
не 1110001000........

```

В этом примере, "и" был в документах 4, 6, 7 и 8, а "в" в документах 0, 1, 6 и 7. Когда мы ищем по всем 3-м полям, мы грузим все 3 карты и выполняем между ними логическую операцию "или" и получаем `1110101110.....`, по мере получения этой биткарты мы начинаем грузить поле сортировки (или расчитывать score) и грузить их значения в очередь с приоритетом, после того как мы прошли все биткарты в очереди остаются записи с самыми высокими значениями ключа сортировки. Эти записи и возвращаются пользователю. При rescore, мы сначала делаем все тоже самое, а потом пересчитываем score для все записей в окне rescore.

Деление на шарды все это дело усложняет, поэтому в вашем случае, делить на 5 шард маленькие индексы особого смысла не имеет.

---

<div class="post-metadata">

### Author: ![Mike\_Patutin](https://avatars.discourse-cdn.com/v4/letter/m/a698b9/32.png) [@Mike\_Patutin](https://discuss.elastic.co/u/Mike_Patutin)
#### Post date: [September 12, 2016, 9:50am UTC](https://discuss.elastic.co/t/c-fuzziness/59692/12 "2016-09-12T09:50:48Z")

</div>

Ну, на мой взгляд, как раз это побитовое сравнение и есть нечто сродни юниону.  
Хорошо. Управлять размером очереди, в которую помещаются значения с данными после расчета приоритета возможно?  
Можно ли ограничить длину этой очереди?

Я попытался сделать так:

```
{
  "query": {
    "filtered": {
      "query": {
        "dis_max": {
          "queries": [
            {
              .....
            },
            {
              .....
            },
            {
              .....
            }
          ]
        }
      },
      "filter": {
        "limit": {
          "value": 3000
        }
      }
    }
  },
  "rescore": [
    {
      .....
    }
  ],
  "from": 0,
  "size": 20
}

```

Но получаю просто первые 3000 подходящих записей, а не 3000 записей с наивысшим score.

Неужели ничего нельзя сделать?

---

<div class="post-metadata">

### Author: ![Igor\_Motov](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/igor_motov/32/45193_2.png) [@Igor\_Motov](https://discuss.elastic.co/u/Igor_Motov)
#### Post date: [September 13, 2016, 7:20pm UTC](https://discuss.elastic.co/t/c-fuzziness/59692/13 "2016-09-13T19:20:16Z")

</div>

> [@Mike\_Patutin](#):
>
> Хорошо. Управлять размером очереди, в которую помещаются значения с данными после расчета приоритета возможно?Можно ли ограничить длину этой очереди?

Да, эта очередь всегда имеет размер `from+size` на каждой шарде - то есть в вашем примере - 20.

> [@Mike\_Patutin](#):
>
> Неужели ничего нельзя сделать?

Поиск по фразам и fuzziness - сложные операции, которые требуют времени. Можно попробовать 2.x, уменьшить количество шард для маленьких индексов, уменьшить количество индексов, увеличить количество нод в кластере.

---

<div class="post-metadata">

### Author: ![Yan\_Work](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/yan_work/32/620_2.png) [@Yan\_Work](https://discuss.elastic.co/u/Yan_Work)
#### Post date: [October 1, 2016, 3:24am UTC](https://discuss.elastic.co/t/c-fuzziness/59692/14 "2016-10-01T03:24:07Z")

</div>

Может быть, если вы хотите сначала видеть последние результаты, то разделить индексы тоже? Индексов будет больше. Например - последний день, последняя неделя, и все остальное. Если большинство поисков будет вам давать то что нужно уже на самом мелком индексе то остальные можно не опрашивать. И данных меньше смотреть.

Ведь пользователь (если он живой) все равно глазами особо далеко не просмотрит и можно остановиться, найдя первые результаты.

p.s. день может быть и год, и месяц, смотря какие данные.

---

<div class="post-metadata">

### Author: ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)
#### Post date: [July 6, 2017, 1:45pm UTC](https://discuss.elastic.co/t/c-fuzziness/59692/15 "2017-07-06T13:45:09Z")

</div>


