Устранение распространенных узких мест производительности Elasticsearch

Практический рабочий процесс для поиска узких мест производительности Elasticsearch в индексации, поиске, куче, хранилище и дизайне шардов.

Устранение распространенных узких мест производительности Elasticsearch

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

Обычно я разделяю вопрос на три части: что медленно, где медленно и что изменилось. «Elasticsearch медленный» — это не действие. «Задержка поиска для logs-prod-* удвоилась после вчерашнего изменения маппинга, в основном на двух узлах данных» дает вам точку для работы.

Диагностика проблем производительности

Прежде чем углубляться в конкретные решения, важно иметь инструменты и методы для диагностики проблем производительности. Elasticsearch предоставляет несколько API и метрик, которые бесценны для этого процесса.

Ключевые инструменты и метрики:

  • API здоровья кластера (_cluster/health): Предоставляет обзор статуса кластера (зеленый, желтый, красный), количество узлов, шардов и ожидающих задач. Большое количество ожидающих задач может указывать на проблемы с индексацией или восстановлением.
  • API статистики узлов (_nodes/stats): Предоставляет подробную статистику для каждого узла, включая использование ЦП, память, дисковый ввод-вывод, сетевой трафик и использование кучи JVM. Это критически важно для выявления узлов, ограниченных ресурсами.
  • API статистики индексов (_stats): Предоставляет статистику для отдельных индексов, такую как скорость индексации, скорость поиска и использование кэша. Это помогает выявить проблемные индексы.
  • Медленный журнал: Elasticsearch может регистрировать медленные запросы на индексацию и поиск. Пороги медленного журнала являются настройками индекса, поэтому вы можете применить их к одному шумному индексу, вместо того чтобы превращать весь кластер в генератор журналов.
    • Медленный журнал индексации: Полезен, когда массовые записи приостанавливаются или скачет задержка приема.
    • Медленный журнал поиска: Полезен, когда вам нужен фактический шаблон запроса, а не просто график задержки.
  • Инструменты мониторинга: Такие решения, как интерфейс мониторинга Kibana, Prometheus с Elasticsearch Exporter или коммерческие инструменты APM, предоставляют панели мониторинга и исторические данные для более глубокого анализа.

Распространенные узкие места и решения

1. Медленная индексация

Медленная индексация может быть вызвана различными факторами, включая задержку в сети, узкие места дискового ввода-вывода, недостаточные ресурсы, неэффективный маппинг или неоптимальное использование Bulk API.

Причины и решения:
  • Насыщение дискового ввода-вывода: Elasticsearch сильно зависит от быстрого дискового ввода-вывода для индексации. Настоятельно рекомендуются SSD.

    • Диагностика: Отслеживайте операции ввода-вывода чтения/записи и пропускную способность с помощью _nodes/stats или инструментов уровня ОС. Ищите высокую глубину очереди.
    • Решение: Перейдите на более быстрое хранилище (SSD), распределите шарды по большему количеству узлов или оптимизируйте стратегию шардирования, чтобы уменьшить ввод-вывод на узел.
  • Давление на кучу JVM: Если куча JVM постоянно находится под давлением, сборка мусора может стать значительным узким местом, замедляя все операции, включая индексацию.

    • Диагностика: Отслеживайте использование кучи JVM в мониторинге Kibana или _nodes/stats. Высокое использование кучи и частые, длительные паузы сборки мусора являются тревожными сигналами.
    • Решение: Увеличьте размер кучи JVM (но не более 50% системной оперативной памяти и не превышая 30,5 ГБ), оптимизируйте маппинги для уменьшения размера документа или добавьте больше узлов для распределения нагрузки.
  • Неэффективный маппинг: Чрезмерно сложные маппинги, динамический маппинг с созданием множества новых полей или неверные типы данных могут увеличить накладные расходы на индексацию.

    • Диагностика: Проанализируйте маппинги индексов (API _mapping). Ищите вложенные объекты, большое количество полей или поля, индексируемые без необходимости.
    • Решение: Определите явные маппинги с соответствующими типами данных. Используйте dynamic: false или dynamic: strict там, где это применимо. Избегайте глубоко вложенных структур, если они не обязательны.
  • Сетевая задержка: Высокая задержка между узлами или между клиентами и кластером может замедлить массовые запросы на индексацию.

    • Диагностика: Измерьте сетевую задержку между вашими клиентами/узлами. Проанализируйте время ответа Bulk API.
    • Решение: Держите узлы кластера в частной сети с низкой задержкой, размещайте массовых клиентов как можно ближе к кластеру и уменьшайте ненужный трафик между регионами. Настройки кэша запросов не исправят сетевую задержку.
  • Неоптимальное использование Bulk API: Отправка отдельных запросов вместо использования массовых запросов или отправка чрезмерно больших/маленьких массовых запросов может быть неэффективной.

    • Диагностика: Отслеживайте пропускную способность вашей массовой индексации. Проанализируйте размер ваших массовых запросов.
    • Решение: Используйте Bulk API для всех операций индексации. Экспериментируйте с размером массовой загрузки (обычно 5-15 МБ на массовый запрос является хорошей отправной точкой), чтобы найти оптимальный баланс между пропускной способностью и задержкой. Убедитесь, что ваши массовые запросы правильно сгруппированы.
  • Долговечность транзакционного журнала: Параметр index.translog.durability контролирует, как часто транзакционный журнал сбрасывается на диск. request (по умолчанию) безопаснее, но может повлиять на производительность по сравнению с async.

    • Диагностика: Это настройка конфигурации.
    • Решение: Для максимальной пропускной способности индексации рассмотрите долговечность async. Однако имейте в виду, что это увеличивает риск потери данных в случае сбоя узла между сбросами.

2. Медленные запросы

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

Причины и решения:
  • Большие шарды: Слишком большие шарды могут замедлить запросы, поскольку Elasticsearch приходится искать по большему объему данных и объединять результаты из большего количества сегментов.

    • Диагностика: Проверьте размеры шардов с помощью _cat/shards или _all/settings?pretty.
    • Решение: Стремитесь к размеру шардов от 10 ГБ до 50 ГБ. Рассмотрите возможность переиндексации данных в новый индекс с меньшими шардами или используйте Index Lifecycle Management (ILM) для управления размером шардов с течением времени.
  • Слишком много шардов: Наличие чрезмерного количества маленьких шардов может привести к высоким накладным расходам для кластера, особенно во время поиска. Каждый шард требует ресурсов для управления.

    • Диагностика: Подсчитайте общее количество шардов на узел и на индекс с помощью _cat/shards.
    • Решение: Объедините индексы, если это возможно. Оптимизируйте свою модель данных, чтобы уменьшить количество индексов и, следовательно, общее количество шардов. Для данных временных рядов ILM может помочь управлять количеством шардов.
  • Неэффективные запросы: Сложные запросы, запросы, включающие тяжелые скрипты, поиск по подстановочным знакам в начале терминов или регулярные выражения, могут быть очень ресурсоемкими.

    • Диагностика: Используйте Profile API (_search?profile=true) для анализа времени выполнения запроса и выявления медленных частей. Анализируйте медленные журналы.
    • Решение: Упростите запросы. Избегайте начальных подстановочных знаков и дорогих регулярных выражений. Используйте запросы term вместо match для точных совпадений, где это возможно. Рассмотрите возможность использования search_as_you_type или completion suggesters для подсказок по мере ввода. Оптимизируйте предложения фильтра (используйте контекст filter вместо контекста query для запросов без оценки).
  • Отсутствие кэширования: Недостаточное или неэффективное кэширование может привести к повторным вычислениям и извлечению данных.

    • Диагностика: Отслеживайте частоту попаданий в кэш для кэша запросов и кэша запросов с помощью _nodes/stats/indices/query_cache и _nodes/stats/indices/request_cache.
    • Решение: Убедитесь, что соответствующее кэширование включено. Кэш фильтров (часть кэша запросов) особенно важен для повторяющихся запросов фильтра. Для часто выполняемых идентичных запросов рассмотрите возможность включения кэша запросов.
  • Накладные расходы на слияние сегментов: Elasticsearch объединяет меньшие сегменты в более крупные в фоновом режиме. Этот процесс потребляет ресурсы ввода-вывода и ЦП, что иногда может влиять на производительность запросов в реальном времени.

    • Диагностика: Отслеживайте количество сегментов на шард с помощью _cat/segments.
    • Решение: Избегайте необдуманного изменения настроек слияния. Во время большой обратной загрузки уменьшите частоту обновления, контролируйте параллелизм массовых операций и следите за регулированием слияния и дисковым вводом-выводом. Принудительные слияния обычно предназначены для индексов, доступных только для чтения, а не для активных горячих индексов.

3. Конкуренция за ресурсы (ЦП, память, сеть)

Конкуренция за ресурсы — это широкая категория, которая может проявляться как в ухудшении производительности индексации, так и запросов.

Причины и решения:
  • Перегрузка ЦП: Высокая загрузка ЦП может быть вызвана сложными запросами, интенсивными агрегациями, слишком большим количеством операций индексации или чрезмерной сборкой мусора.

    • Диагностика: Отслеживайте использование ЦП на узел (_nodes/stats). Определите, какие операции потребляют больше всего ЦП (например, поиск, индексация, сборка мусора JVM).
    • Решение: Оптимизируйте запросы и агрегации. Распределите нагрузку по большему количеству узлов. Уменьшите скорость индексации, если она перегружает ЦП. Убедитесь, что настройки кучи JVM адекватны, чтобы минимизировать накладные расходы на сборку мусора.
  • Проблемы с памятью (куча JVM и системная память): Недостаточная куча JVM приводит к частой сборке мусора. Нехватка системной памяти может вызвать подкачку, что резко снижает производительность.

    • Диагностика: Отслеживайте использование кучи JVM и общую системную память (ОЗУ, подкачку) на каждом узле.
    • Решение: Выделите достаточную кучу JVM (например, 50% системной оперативной памяти, до 30,5 ГБ). Избегайте подкачки, обеспечив достаточный объем свободной системной памяти. Рассмотрите возможность добавления большего количества узлов или использования выделенных узлов для определенных ролей (master, data, ingest).
  • Сетевые узкие места: Высокий сетевой трафик может замедлить межузловое взаимодействие, репликацию и клиентские запросы.

    • Диагностика: Отслеживайте использование пропускной способности сети и задержку между узлами и клиентами.
    • Решение: Оптимизируйте сетевую инфраструктуру. Уменьшите ненужную передачу данных. Убедитесь в оптимальных настройках распределения шардов и репликации.
  • Насыщение дискового ввода-вывода: Как упоминалось в разделе об индексации, это также влияет на производительность запросов при чтении данных с диска.

    • Диагностика: Отслеживайте метрики дискового ввода-вывода.
    • Решение: Перейдите на более быстрое хранилище, распределите данные по большему количеству узлов или оптимизируйте запросы, чтобы уменьшить объем читаемых данных.

Лучшие практики для настройки производительности

  • Постоянный мониторинг: Настройка производительности — это непрерывный процесс. Регулярно отслеживайте состояние вашего кластера и использование ресурсов.
  • Оптимизация маппингов: Определите явные, эффективные маппинги, адаптированные к вашим данным. Избегайте ненужных полей или индексации.
  • Стратегия шардирования: Стремитесь к оптимальному размеру шардов (10-50 ГБ) и избегайте слишком большого или слишком малого количества шардов.
  • Используйте Bulk API: Используйте Bulk API для индексации и multi-search API, когда вам нужно объединить независимые поиски.
  • Настройте кучу JVM: Выделите достаточный объем кучи, но не перераспределяйте. Избегайте подкачки.
  • Понимайте производительность запросов: Профилируйте запросы, упрощайте их и используйте контекст фильтра.
  • Используйте кэширование: Убедитесь, что кэши запросов и кэши запросов используются эффективно.
  • Оборудование: Используйте SSD для хранения данных и обеспечьте достаточный объем ЦП и ОЗУ.
  • Выделенные узлы: Рассмотрите возможность использования выделенных узлов для ролей master, data и ingest для изоляции рабочих нагрузок.
  • Index Lifecycle Management (ILM): Для данных временных рядов ILM необходим для управления индексами, переключения шардов и, в конечном итоге, удаления старых данных, что помогает контролировать количество и размер шардов.

Когда вы находите узкое место, внесите наименьшее изменение, которое напрямую его устраняет. Добавляйте узлы, когда в кластере действительно не хватает емкости. Исправляйте маппинги, когда тратится куча. Переписывайте запросы, когда вывод профиля указывает на дорогие условия. Корректируйте стратегию шардирования, когда один узел выполняет работу, которая должна быть распределена. Эта дисциплина не позволяет работе по повышению производительности превратиться в кучу несвязанных ручек настройки.