Информационный поиск

Distributed Search: Elasticsearch

Когда Github перевёл код-поиск на Elasticsearch в 2021 году, индекс составил 15 ТБ кода. Один узел вместил бы 0.3% этого объёма. Без шардирования Github Code Search не существовал бы.

  • **Wikipedia Search:** 6.7 млн статей в Elasticsearch с 5 шардами и 1 репликой - поиск по всему корпусу за 50-200 мс
  • **E-commerce каталоги:** Zalando использует Elasticsearch для поиска по 500+ тыс. товаров с real-time обновлением цен и остатков через агрегации
  • **Log aggregation:** ELK Stack (Elasticsearch + Logstash + Kibana) - стандарт для сбора и анализа логов в enterprise, миллиарды событий в день

Шардирование индекса

Wikipedia в Elasticsearch содержит 6,7 млн статей - больше не влезет в один узел ни по памяти, ни по диску. Шардирование решает это разбивая индекс на N независимых шардов, каждый из которых - полноценный инвертированный индекс на отдельном узле. При поиске координатор рассылает запрос на все шарды параллельно и мержит результаты. Число шардов задаётся при создании индекса и не меняется без reindex.

Правило выбора числа шардов: целевой объём индекса / 20-40 ГБ на шард. Слишком мало шардов - узкое место при масштабировании; слишком много - overhead на координацию запросов и сборку мусора JVM. Оптимальный размер шарда для Elasticsearch: 10-50 ГБ.

Почему нельзя изменить число первичных шардов в существующем индексе Elasticsearch без reindex?

Репликация и отказоустойчивость

Каждый первичный шард в Elasticsearch имеет N реплик - синхронные копии на других узлах. При записи документа: (1) координатор роутит на первичный шард, (2) первичный записывает локально и параллельно реплицирует на все реплики, (3) только после подтверждения всех реплик возвращает ack клиенту. При падении узла с первичным шардом одна из реплик автоматически повышается до первичной.

Параметр wait_for_active_shards управляет балансом между latency и durability: 1 = ack только от первичного (быстро, риск потери при сбое), all = ждать все реплики (медленно, максимальная надёжность). По умолчанию = 1.

Что означает статус 'yellow' в Elasticsearch cluster health?

Роутинг и custom routing

По умолчанию Elasticsearch роутит документы равномерно: shard = hash(doc_id) % num_primary_shards. Запрос к индексу идёт на все шарды - scatter-gather. Custom routing позволяет гарантировать, что связанные документы попадут на один шард: тогда запрос идёт только на один шард вместо всех. Для multi-tenant систем это снижает latency в N раз, где N - число шардов.

Custom routing создаёт риск 'горячих шардов' (hot shards): если все документы одного tenant активно запрашиваются, один шард перегружен. Решение - routing_partition_count: документы tenant роутятся не на один шард, а на подмножество шардов.

Какое главное преимущество custom routing в Elasticsearch для multi-tenant систем?

Агрегации и аналитические запросы

Агрегации - аналог GROUP BY в SQL, но выполняемый параллельно на всех шардах. Каждый шард вычисляет частичный результат, координатор их мержит. Bucket aggregations группируют документы (terms, histogram, date_histogram). Metric aggregations вычисляют статистики (avg, sum, percentiles). Pipeline aggregations применяются к результатам других агрегаций.

Terms aggregation собирает топ-N значений по шардам с погрешностью: каждый шард возвращает свой топ-N, координатор мержит. Реальное топ-N по всему индексу может отличаться. Параметр shard_size увеличивает точность за счёт объёма передаваемых данных.

Elasticsearch агрегации точны как SQL GROUP BY

Terms aggregation возвращает приближённый топ-N из-за распределённой природы вычислений - каждый шард видит только свои данные

SQL GROUP BY выполняется на полных данных централизованно. В distributed aggregation каждый шард возвращает частичный результат; мерж частичных топ-N не гарантирует глобальный топ-N. Параметр shard_size увеличивает точность, но ценой трафика и памяти

Почему terms aggregation в Elasticsearch может возвращать неточный топ-N?

Ключевые идеи

  • **Шардирование:** индекс разбивается на N шардов, число фиксировано при создании - формула hash(id) % N определяет шард документа
  • **Репликация:** каждый первичный шард имеет реплики на других узлах; yellow = реплики недоступны, red = первичные шарды недоступны
  • **Custom routing + агрегации:** routing ограничивает scatter-gather до одного шарда; terms aggregation приближённая - shard_size повышает точность

Связанные темы

Distributed Elasticsearch строится поверх базовых концепций поиска:

  • Инвертированный индекс — Каждый шард Elasticsearch - это независимый Lucene индекс с собственными инвертированными списками
  • Search Infrastructure at Scale — Tiering, caching и federation - следующий уровень масштабирования поверх базового кластера ES

Вопросы для размышления

  • Число шардов нельзя изменить без reindex. Как бы спроектировали начальную конфигурацию для проекта, объём данных которого непредсказуем?
  • Terms aggregation даёт приближённый результат. В каких бизнес-сценариях эта погрешность критична - и есть ли workarounds для получения точных результатов?
  • Custom routing ускоряет запросы для конкретного tenant, но создаёт риск hot shards. Как бы сбалансировали эти трейдоффы для системы с 10 крупными tenant и 10 000 мелкими?

Связанные уроки

  • dist-14-sharding
Distributed Search: Elasticsearch

0

1

Войти