Типичные ошибки: text/keyword, mapping explosion, тяжёлые агрегации

Собираем главные грабли Elasticsearch в один урок, чтобы вы наступали на них в учебнике, а не на проде.

Большинство проблем с ES сводятся к трём причинам: неверный тип поля, неконтролируемый рост маппинга и слишком тяжёлые агрегации.

Ошибка 1: путаница text и keyword

Самая частая ошибка новичков. Признаки беды:

  • «Поиск ничего не находит». Обычно это term по полю text (значение в индексе — проанализированный терм, а term ищет буквально) — мы разбирали это в уроке про match/term.
  • «Не получается отсортировать/сгруппировать». Сортировка и terms-агрегация по анализируемому text не работают (ошибка про fielddata).

Решение — multi-field: индексировать поле и как text (для поиска), и как keyword (для точных операций):

{
  "mappings": {
    "properties": {
      "category": {
        "type": "text",
        "fields": {
          "raw": { "type": "keyword" }
        }
      }
    }
  }
}

Тогда поиск идёт по category, а группировка и сортировка — по category.raw. Это стандартная практика.

Ошибка 2: mapping explosion

Динамический маппинг удобен, но опасен: если в документах появляются всё новые поля (например, ключи приходят из пользовательских данных или из имён метрик), маппинг разрастается до тысяч полей. Каждое поле — это память и накладные расходы; при тысячах полей кластер деградирует и может упасть. Это и есть mapping explosion.

Защита:

  • задавать маппинг явно и не плодить поля;
  • отключать динамику там, где она не нужна: "dynamic": "strict" (новое поле — ошибка) или "dynamic": false (поле игнорируется в индексе);
  • не превращать произвольные ключи данных в имена полей; хранить такие пары как flattened или массив объектов {key, value}.

Ошибка 3: тяжёлые агрегации

Агрегации мощные, но дорогие. Что бьёт по кластеру:

  • Высокая кардинальность. terms по полю с миллионами уникальных значений (id пользователей) строит огромное число бакетов и ест память.
  • Глубокая вложенность. Агрегации в агрегациях множат число корзин комбинаторно.
  • Большой временной диапазон + мелкий интервал. date_histogram по минутам за год — миллионы бакетов.

Лечится сужением диапазона (фильтром по дате), разумным size у terms, и пониманием, что точный подсчёт уникальных дорог — для прикидки есть cardinality (приблизительная, но дешёвая).

Как работает под капотом

Все три ошибки — про ресурсы. Тип поля определяет, какие структуры строятся: text даёт инвертированный индекс (поиск), но не doc values для сортировки; keyword — наоборот. Маппинг целиком держится в памяти кластера (cluster state) и рассылается между узлами — поэтому тысячи полей его раздувают и замедляют управление кластером. Агрегации считаются в памяти на каждом шарде: число бакетов напрямую определяет потребление памяти, и при взрывном росте узел упирается в circuit breaker или падает по OOM.

Частые ошибки

  • Не закладывать keyword-подполе сразу. Потом окажется, что по полю нельзя сортировать/группировать, и придётся переиндексировать.
  • Оставлять dynamic по умолчанию на пользовательских данных. Прямой путь к mapping explosion.
  • Агрегировать «всё и сразу». Всегда ограничивайте диапазон фильтром и кардинальность параметром size.

Итоги

  • text для поиска, keyword для точных операций; используйте multi-field, чтобы иметь и то, и другое.
  • Mapping explosion от динамических полей рушит кластер — задавайте маппинг явно и ограничивайте динамику.
  • Тяжёлые агрегации (высокая кардинальность, глубокая вложенность, мелкие интервалы) едят память — сужайте диапазон и size.
Проверьте себя
1. Как одновременно искать по полю полнотекстово и сортировать/группировать по нему?
AИспользовать только text
BИспользовать multi-field: основное поле как text, а подполе (.raw) как keyword
CИспользовать только keyword
DЭто невозможно
2. Что такое mapping explosion и чем он опасен?
AВзрывной рост числа документов
BНеконтролируемый рост числа полей в маппинге (часто из динамического маппинга), который раздувает cluster state и может уронить кластер
CСлишком большие шарды
DОшибка в Query DSL