elasticsearch что это такое
Почему Elasticsearch — хороший выбор для сбора и анализа данных среднего объёма
Авторизуйтесь
Почему Elasticsearch — хороший выбор для сбора и анализа данных среднего объёма
Рассказывает Франсуа Руа, руководитель отдела разработки ГК «Авилекс»
Контекст задачи
Когда ваш бизнес предполагает анализ статистических данных, поступающих из разных источников, вам требуется эти данные собирать, хранить, индексировать, трансформировать в другие данные, анализировать и т. д.
Часто бывает так, что масштаб проекта ещё недостаточно велик для внедрения крупных программных платформ наподобие Hadoop, и в этом случае вам помогут универсальные варианты на базе стандартных NoSQL-решений, которые позволят справиться с накоплением и обработкой данных среднего объёма.
К таким решениям, исходя из нашей практики, относится Elasticsearch.
Что такое Elasticsearch
Elasticsearch — это представитель кластерных NoSQL с JSON REST API.
18–20 ноября, Онлайн, Беcплатно
Мы можем считать его и нереляционным хранилищем документов в формате JSON, и поисковой системой на базе полнотекстового поиска Lucene.
Аппаратная платформа — Java Virtual Machine.
Официальные клиенты доступны на Java, NET (C#), Python, Groovy, JavaScript, PHP, Perl, Ruby.
Elasticsearch разрабатывается компанией Elastic вместе со связанными проектами, называемыми Elastic Stack, — Elasticsearch, Logstash, Beats и Kibana.
Beats — легковесные агенты и отправители данных с различных устройств. Logstash собирает и обрабатывает данные зарегистрированных событий. За хранение и поиск данных отвечает Elasticsearch. Kibana визуализирует данные через web-интерфейс.
Сегодня Elastic Stack с успехом используется сервисами eBay, Adobe, Uber, Nvidia, Blizzard, Citibank, Volkswagen, Microsoft, SoundCloud, GitHub, Netflix, Amazon. Чем же привлекателен Elasticsearch в контексте поставленной задачи? Давайте разберёмся.
Простой выбор
Одним из пунктов технического задания в рамках нашего проекта было требование собирать и анализировать статистику примерно с 25 (+/- 5) тысяч различных устройств.
Аппаратные возможности, операционные системы, сетевые интерфейсы, типы и назначение устройств неоднородны — от смартфона и телевизора до инфраструктурного сервера.
Устройства находятся в отдельных зданиях (примерно 1500 зданий, в каждом от 10 до 20 устройств), обслуживаются однотипной, но изолированной от других зданий инфраструктурой.
Оценив поставленную задачу, мы поняли, что нам не нужна большая суперсистема, которую можно отнести к категории BigData и/или HighLoad. С другой стороны, любые привычные методы сохранения и обработки информации, такие как запись в текстовый файл или SQL-базу, не подходили из-за объёма и специфики данных, поскольку большая часть работы происходила с логами устройств. Сыграло свою роль и наличие дополнительной статистики, которую сообщают сервисы, запущенные на устройствах.
Также в нашем случае по оценке объёма входящих данных, скорости их поступления и озвученных задач аналитики не было необходимости отдельно строить OLTP- и OLAP-системы.
Другими словами, система предполагает сбор статистики, к тому же она обеспечивает некоторое накопление данных и показ этой истории в удобном и интересном для менеджеров и аналитиков проекта виде. В результате мы выбрали Elasticsearch как оптимальное решение.
Да и Elastic Stack в целом предназначен для решения такого класса задач.
А что, собственно, собираем?
Как говорилось ранее, устройства разные, а вот статистическая информация нас, как правило, интересует достаточно однотипная: температура и загрузка процессора, объём потребляемой памяти, время и режимы использования устройства, какие программы запускались, сетевой трафик, сколько задач выполнено, что в логи записано, какие ошибки зарегистрированы и прочие данные с устройства и об устройстве.
Что на базе собранной информации хотят получить аналитики и менеджеры?
Самый частый из встречающихся сценариев — он же был изначально озвучен в техническом задании — это сбор и хранение всей (сырой) статистики по всем устройствам и сервисам за последний месяц с последующей агрегацией по дням и группировкой по зданиям с «бессрочным» хранением полученного результата.
Raw-индексы перезаписываются каждый месяц новыми данными, Agg-индексы накапливаются по дням «бесконечно» (пока хватает дискового пространства).
Все остальные пожелания по группировке и разбивке данных, по аналитическим срезам, визуальному представлению и т. п. выполняются аналитиками и менеджерами самостоятельно с использованием как Kibana, так и Power BI.
Периодически некоторые данные, чаще всего новые, получаемые из исходных, выделяются в отдельную задачу предварительного расчёта, которая выполняется с помощью вычислительной платформы Spark «по расписанию» и сохраняется в ещё один Agg-индекс, откуда эти подготовленные данные попадают в сложные отчёты и т. д.
Немного фактов о системе
Elasticsearch, как выяснилось, прекрасно подходит для работы в пределах определённого объёма данных (2–10 терабайт в год, 20–30 миллиардов документов в индексах), а также хорошо интегрируется с кластером Spark.
Агенты (Beats) помогают на конкретном устройстве или конкретном сервере собрать информацию, которая интересует пользователей системы. С помощью этих агентов можно собирать разного рода данные: системную информацию Windows из журнала, логи операционной системы Linux, данные устройства на ОС Android, самим анализировать трафик с устройства, будь то TCP, HTTP и т. д.
Локальный для инфраструктуры каждого здания Logstash отлично справляется с отправкой данных, собираемых агентами устройств, в централизованный кластер Elasticsearch, а Kibana предоставляет удобный способ построения веб-отчётов.
Необходимые инфраструктурные ресурсы
В нашем случае используется Linux-кластер в составе 3–10 нод.
Нода — это 8 процессорных ядер, 16–32 гигабайта оперативной памяти, жёсткий диск размером 1–5 терабайт. Сеть 1 Гигабит.
Масштабируемость
Данная подсистема статистики может работать с любой сферой деятельности, где требуется сбор и анализ статистических данных среднего объёма. Это может быть обработка статистической информации с 1 000 и до 30 000 холодильников, мобильных устройств, ноутбуков, интерактивных панелей и т. д.
Когда устройств меньше, чем 1–3 тысячи, система избыточна, есть более простые решения. Количество в 10 000–30 000 единиц оптимально по объёму и скорости появления новых данных с устройств.
50 и более тысяч устройств повлекут за собой усложнение системы, и в этом случае надо выбирать другое решение.
Хотя, если мы воспринимаем 50–100 тысяч устройств как три сегмента по 15–30 тысяч, то можно просто запустить три подсистемы нашей статистики.
Основная идея заключается в том, что чем более изолированы «сектора», тем проще применить решение формата «три по тридцать».
Заключение
На примере проекта городского масштаба мы рассмотрели применение Elasticsearch для работы с большими данными, оценили его преимущества и целесообразность применения для задач, где массивные решения вроде Hadoop избыточны.
Учимся работать с Elasticsearch
Содержание статьи
Основных поисковых брендов на данный момент существует несколько: это Solr, Sphinx, Elasticsearch. Но сегодня мы поговорим только о последнем. Elasticsearch — это на самом деле не вполне самостоятельный поиск. Это, скорее, красивая обертка над библиотекой Apache Lucene (на нем же строится Solr). Но не стоит воспринимать слово «обертка» в негативном ключе. Lucene сам по себе вообще мало на что годен. Это все-таки не полноценный сервис, а просто библиотека для построения поисковых систем. Все, что она может, — только индексировать и искать. А API для ввода данных, для поисковых запросов, кластеризация и прочее — это все отдается на откуп «обертке».
Что нам дает Elasticsearch?
Масштабируемость и отказоустойчивость. Elasticsearch легко масштабируется. К уже имеющейся системе можно на ходу добавлять новые серверы, и поисковый движок сможет сам распределить на них нагрузку. При этом данные будут распределены таким образом, что при отказе какой-то из нод они не будут утеряны и сама поисковая система продолжит работу без сбоев.
На самом деле оно даже работает. В хипстерском стиле «чувак, вот тебе три команды — пользуйся ими и, пожалуйста, не задумывайся, какой ад происходит внутри». И часто это прокатывает. Новые ноды подключаются буквально парой строчек в конфиге, почти как у Redis. Главное, мастеры со слейвами не путать, а то он возьмет и молча потрет все данные :). При выпадении каких-либо серверов из кластера, если правильно были распределены реплики данных, корректно настроенное приложение продолжит поиск, как будто ничего не произошло. После того как сервер поднимется, он сам вернется в кластер и подтянет последние изменения в данных.
Мультиарендность (англ. multitenancy) — возможность организовать несколько различных поисковых систем в рамках одного объекта Elasticsearch. Причем организовать их можно абсолютно динамически. Очень интересная особенность, которая в отдельных случаях становится определяющей при выборе поисковой системы. На первый взгляд может показаться, что необходимости в этой особенности нет. Классические системы поиска типа Sphinx обычно индексируют какую-то одну базу с определенным кругом данных. Это форумы, интернет-магазины, чаты, различные каталоги. Все те места, где поиск для всех посетителей должен быть идентичным. Но на самом деле довольно часто возникают ситуации, когда систем поиска должно быть больше одной. Это либо мультиязычные системы, либо системы, где есть определенное количество пользователей, которым нужно предоставлять возможность поиска по их персональным данным.
В первом случае нам нужно строить отдельные индексы по разным языкам, отдельно настраивать морфологию, стемминг, параметры нечеткого поиска для того, чтобы получить максимально качественные результаты для каждого из языков. Во втором случае в качестве гипотетического примера можно взять какой-нибудь корпоративный аналог Dropbox’а. Приходит клиент, регистрируется, заливает свои документы. Система их анализирует, угадывает язык, парсит, заливает в отдельный индекс поисковой системы, настраивает параметры под нужный язык. И далее клиент может пользоваться поиском по своим документам. Поиск будет работать достаточно быстро, потому что данных в индексе отдельного клиента всегда будет меньше, чем в одном большом общем, будет возможность динамически такие индексы создавать, устанавливать различные поисковые параметры. Ну и данные клиентов будут изолированы друг от друга.
Операционная стабильность — на каждое изменение данных в хранилище ведется логирование сразу на нескольких ячейках кластера для повышения отказоустойчивости и сохранности данных в случае разного рода сбоев.
Отсутствие схемы (schema-free) — Elasticsearch позволяет загружать в него обычный JSON-объект, а далее он уже сам все проиндексирует, добавит в базу поиска. Позволяет не заморачиваться слишком сильно над структурой данных при быстром прототипировании.
RESTful api — Elasticsearch практически полностью управляется по HTTP с помощью запросов в формате JSON.
Краткий словарик начинающего гуманитария
Установка и использование
Установить Elasticsearch проще простого. Есть готовые репозитории и для RHEL/Centos, и для Debian. Можно отдельно установить из тарбола.
Хакер #196. Все о Docker
И вся дальнейшая работа с ним происходит посредством HTTP-запросов в JSON-формате. Давай, к примеру, создадим новый индекс и забьем в него какие-нибудь тестовые данные. Я взял отсюда англо-русский параллельный корпус, собранный из данных OpenSubtitles.org. Формат TMX достаточно простой, описывать его отдельно не стану. Напишу небольшой парсер на Python, который бы разбирал файл и заливал данные в новый индекс:
На VPS’ке с четырьмя гигами памяти во флопсе заливка четырех с половиной миллионов документов (чуть больше 900 Мб данных в текстовом формате) занимает примерно полтора часа. В целом очень даже неплохо. Теперь накидаем небольшой скриптик для удобного поиска:
И проверяем, что у нас получилось:
Первая колонка — вес полученного значения, остальные две — найденные результаты. А теперь ищем по-русски:
Как видишь, неплохо ищет уже прямо из коробки, для какого-нибудь блога или небольшого форума вполне подойдет. А если качество выдачи покажется недостаточно высоким (а к такой мысли рано или поздно приходят почти все), то Elasticsearch предоставляет большое количество возможностей для дальнейшего тюнинга анализаторов и поисковых алгоритмов.
Анализаторы
Выбор правильного анализатора для обработки своих данных — это что-то почти на грани искусства. Изнутри каждый анализатор представляет собой своеобразный конвейер, состоящий из нескольких обработчиков:
Главная цель любого анализатора — из длинного предложения, перегруженного ненужными деталями, выжать основную суть и получить список токенов, которые бы ее отражали.
Конвейер анализатора
Примерную схему работы конвейера можно увидеть на картинке поблизости. Анализ начинается с опциональных символьных фильтров. Это, к примеру, перевод текста в нижний регистр или подстановка слов. Полученный результат передается токенизатору, главному и единственному обязательному элементу анализатора. Здесь предложение очищается от знаков препинания, разбивается на отдельные слова-токены, которые могут либо сохранять имеющуюся форму, либо обрезаться только до основы слова, либо обрабатываться еще каким-либо образом в зависимости от токенизатора. После токенизатора полученные данные отправляются на дальнейшую фильтрацию, если уже проделанных манипуляций будет недостаточно.
Elasticsearch из коробки предоставляет сразу несколько различных анализаторов. Если их будет мало, то нестандартные анализаторы можно будет добавить с помощью специального API. Вот базовый пример нестандартного анализатора:
Что делает этот анализатор:
И смотри, как это выглядит на живом примере. Возьмем предложение «Мама мыла раму, пока собака доедала сосиску» и разберем его по пунктам (рис. «Мама мыла-мыла. »).
Детальнее о предоставляемых вместе с Elasticsearch анализаторах и фильтрах можно прочитать в официальной документации. Здесь описывать не возьмусь, так как деталей там очень много.
Нечеткий поиск
Обработка естественных языков — это работа с постоянными неточностями. По большей части поисковые движки пытаются анализировать грамматические структуры различных языков, осваивать определенные паттерны, характерные для того или иного языка. Но поисковая система постоянно сталкивается с запросами, выходящими за рамки устоявшихся правил орфографии и морфологии. Чаще всего это либо опечатки, либо банальная безграмотность. Самый простой пример нечеткого поиска — это знаменитое «Возможно, Вы имели в виду. » в Гугле. Когда человек ищет «пгода вИ кутске», а ему показывают погоду в Иркутске.
Основой нечеткого поиска является расстояние Дамерау — Левенштейна — количество операций вставки/удаления/замены/транспозиции для того, чтобы одна строка совпала с другой. Например, для превращения «пгода вИ кутске» в «погода в Иркутске» такое расстояние было бы равно трем — две вставки и одна замена.
Расстояние Дамерау — Левенштейна — это модификация классической формулы Левенштейна, в которой изначально отсутствовала операция транспозиции (перестановки двух соседних символов). Elasticsearch поддерживает возможность использования в нечетком поиске обоих вариантов, по умолчанию включено использование расстояния Дамерау — Левенштейна.
При работе с нечетким поиском также не стоит забывать и о том, как Elasticsearch (да и любой другой поисковый движок в принципе) работает изнутри. Все данные, загружаемые в индекс, сперва проходят обработку анализатором, лемматизацию, стемминг. В индекс уже складываются только «обрывки» исходных данных, содержащие максимум смысла при минимуме знакового объема. И уже по этим самым обрывкам впоследствии проводится поиск, что при использовании нечеткого поиска может давать довольно курьезные результаты.
Например, при использовании анализатора snowball во время нечеткого поиска по слову running оно после прохода через стемминг превратится в run, но при этом по нему не найдется слово runninga, так как для совпадения с ним нужно больше двух правок. Поэтому для повышения качества работы нечеткого поиска лучше использовать самый простой стеммер и отказаться от поиска по синонимам.
Elasticsearch поддерживает несколько различных способов нечеткого поиска:
CJK — это три буквы боли западных систем полнотекстового поиска и людей, которые хотят ими воспользоваться. CJK — это сокращение для Chinese, Japanese, Korean. Три основных восточных языка, составляющих совокупно почти 10% современного интернета. Они отличаются от привычных западных языков практически всем — и письменностью, и морфологией, и синтаксисом. Все это, понятно, вызывает некоторые проблемы при разработке различных систем обработки естественных языков, в том числе и поисковых систем.
У Elasticsearch в этой области дела тоже обстоят неплохо. Есть встроенный анализатор CJK со стеммингом, есть возможность использовать нечеткий поиск. Вот только если по текстам на корейском и японском языках еще хоть как-то можно искать «по классическим правилам» (то есть делим на слова, отбрасываем союзы/предлоги, оставшиеся слова токенизируем и загоняем в индекс), то вот с китайским, в котором слова в предложении не принято разделять пробелами, все куда сложнее.
Для поисковой системы все предложение на китайском остается одной целой единицей, по которым проводится поиск. Например, предложение «Мэри и я гуляем по Пекину» выглядит вот так:
Девять символов без пробелов, 18 байт в UTF-8. В нормальной вселенной это прокатило бы за одно слово, но не тут. Если стратегически расставить пробелы в нужных местах, то предложение станет выглядеть вот так:
Шесть слов. С этим уже можно было бы работать. Вот только пробелы в китайском никто не использует. Можно пытаться разделять предложения на слова в автоматическом режиме (уже даже существует пара готовых решений), но и тут тебя будут ожидать неприятности. Некоторые слоги, стоящие в предложении рядом, могут, в зависимости от того, как их разделить пробелами, складываться в разные слова и резко менять смысл предложения. Возьмем для примера предложение 我想到纽约:
Как видишь, на автоматизированное членение лучше не полагаться. Как тогда быть? Тут нам поможет поиск по N-граммам. Предложение делится на куски по два-три знака:
И уже по ним далее идет поиск. К этому можно добавить нечеткий поиск с расстоянием в одну-две замены, и уже получится более-менее сносный поиск.
Безопасность
У Elasticsearch нет никакой встроенной системы авторизации и ограничения прав доступа. После установки он по умолчанию вешается на порт 9200 на все доступные интерфейсы, что делает возможным не только полностью увести у тебя все, что находится в поисковой базе, но и, чисто теоретически, через обнаруженную дыру залезть в систему и там начудить. До версии 1.2 такая возможность была доступна прямо из коробки (см. CVE-2014-3120) и напрягаться не было вообще никакой нужды. В 1.2 по умолчанию выполнение скриптов в поисковых запросах отключено, но пока что и это не спасает.
Совсем недавно мы наблюдали ботнет на эластиках версий в том числе и 1.4 и выше. Судя по всему, использовалась уязвимость CVE-2015-1427. В версии 1.4.3 ее вроде как закрыли, но, сам понимаешь, полагаться на удачу в таких делах не вариант (на самом деле да, пока писалась эта статья, свежепоставленный эластик версии 1.5.0 на тестовых виртуалках у меня успели поломать уже на второй день :)). Вешай сервис только на локальные IP, все необходимые подключения извне ограничивай только доверенными адресами, фильтруй поисковые запросы, своевременно обновляйся. Спасение утопающих — дело рук самих утопающих.
К теме сохранности данных также стоит упомянуть про бэкапы. Возможности резервного копирования и восстановления встроены в сам Elasticsearch, причем довольно интересно. Перед началом создания резервных копий нужно эластику сообщить, куда они будут складываться. В местных терминах это называется «создать репозиторий»:
После того как создан репозиторий, можно начать бэкапиться:
Такой запрос создает бэкап с названием snapshot_1 в репозитории my_backup.
Восстановить данные можно следующим образом:
Причем снимки состояния делаются инкрементальные. То есть в первый раз создается полный бэкап, а далее при последующих бэкапах фиксируется только разница состояния между текущим моментом и моментом предыдущего бэкапа. Если у тебя кластер с несколькими мастерами, то хранилище репозитория должно шариться между всеми мастерами (то есть, при хранении на файловой системе, это должен быть какого-либо рода сетевой диск, доступный всем мастерам). Файлы репозитория я бы тоже с диска куда-нибудь бэкапил на всякий случай :).
Эпилог
На этом, наверное, стоит пока остановиться. К сожалению, за бортом статьи остались животрепещущие детали того, как на самом деле работает кластеризация и действительно ли Elasticsearch такой неубиваемый, как его хвалят. Не было сказано совсем ничего про систему плагинов и различные веб-панели для удобного администрирования поискового кластера. Но и без этого Elasticsearch уже выглядит достаточно интересным, чтобы продолжить с ним знакомство самостоятельно и, возможно, найти для себя идеальный поиск.
Elasticsearch что это такое
Elasticsearch – это одна из самых популярных поисковых систем в области Big Data, масштабируемое нереляционное хранилище данных с открытым исходным кодом, аналитическая NoSQL-СУБД с широким набором функций полнотекстового поиска.
Назначение и основные функциональные возможности
Elasticsearch (ES) – масштабируемая утилита полнотекстового поиска и аналитики, которая позволяет быстро в режиме реального времени хранить, искать и анализировать большие объемы данных. ES является ядром ELK-стека (Elastic Stack), в состав которого, помимо Elasticsearch, входят следующие продукты [1]:
Из ключевых функциональных возможностей Elasticsearch стоит отметить следующие [2]:
Основные достоинства и недостатки Elasticsearch описаны здесь. Подчеркнем, что одним из главных недостатков ES считается склонность этой NoSQL-СУБД к утечкам данных из-за отсутствия встроенных средств обеспечения информационной безопасности, таких как система авторизации и ограничения прав доступа. Кроме того, после установки движок по умолчанию связывается с портом 9200 на все доступные интерфейсы, что открывает доступ к базе данных [2]. Подробнее об уязвимостях Elasticsearch читайте нашу отдельную статью.
История разработки и развития Elasticsearch
Основными ключевыми вехами в истории Elasticsearch считаются следующие:
Архитектура и принципы работы ES
ES обеспечивает горизонтально масштабируемый поиск с поддержкой многопоточности. Система основана на библиотеке Apache Lucene, которая предназначена для индексирования и поиска информации в любом типе документов. Все функции Lucene доступны через API-интерфейсы на JSON и Java. ES позволяет работать с GET- запросами в реальном времени, но не поддерживает распределённые транзакции. Бесшовная интеграция с Kibana гарантирует легкую управляемость по HTTP-интерфейсу с помощью JSON-запросов за счет REST API.
В масштабных Big Data системах несколько копий Elasticsearch объединяются в кластер. Поисковые индексы можно разделить на сегменты, реплицировав каждый из которых несколько раз. Это обеспечивает отказоустойчивость системы. На узле ES-кластера может размещаться несколько сегментов. Каждый узел кластера действует как координатор для делегирования операций правильному сегменту с автоматической перебалансировкой и маршрутизацией. Связанные данные часто хранятся в одном и том же индексе из одного или нескольких первичных сегментов и нескольких реплик. После создания индекса количество первичных сегментов нельзя изменить. Долгосрочное хранение индекса обеспечивает шлюз, позволяя восстанавливать индекс при сбое сервера [5].
Принцип работы ELK-стека
С чего начинается Elasticsearch
Elasticsearch, вероятно, самая популярная поисковая система на данный момент с развитым сообществом, поддержкой и горой информации в сети. Однако эта информация поступает непоследовательно и дробно.
Самое первое и главное заблуждение — «нужен поиск, так бери эластик!». Но в действительности, если вам нужен шустрый поиск для небольшого или даже вполне себе крупного проекта, вам стоит разобраться в теме поподробней и вы откажетесь от использования именно этой системы.
Вторая проблема заключается в том, что пытаясь разобраться с начала, получить общую картину окажется непросто. Да инфы навалом, но последовательность в ее изучении выстраивается постфактум. Придется из книг бежать в документацию, а из документации обратно в книги, параллельно разгугливая тонкости, только чтобы понять, что такое Elasticsearch, почему он работает именно так и для чего же его вообще использовать, а где стоит выбрать что-то попроще.
В этой статье я попытался последовательно объяснить то что мне кажется главным в Elasticsearch, то для чего он был придуман и как он устроен.
Для наглядности выдумаем себе задачу. Реализация поиска в коллективном блоге по всем материалам и пользователям. Система позволяет создавать теги, сообщества, геометки и все остальные штуки, которые нам помогают категоризировать огромное количество информации.
Схема хранения данных
То, какие действия с данными мы будем производить определит схему их хранения:
Представьте еще раз сколько атрибутов может иметь публикация и сколько связанных с ней объектов. Автор, категория, сообщество, геометки, медиафайлы, теги, связанные публикации. Этот список можно продолжать до исчерпания фантазии. Если мы храним это в привычной реляционной базе то имеем миллион связей и миллиард атрибутов. Это прекрасно подходит для структурированного хранения долгие годы, но не очень вяжется с требованиями быстрого поиска.
А что если мы захотим добавить пару интеграций с внешними системами? Придется реализовать дополнительные таблицы или даже базы. Нам всегда будет нужно что-то добавить или изменить в объектах доступных для поиска. Вы понимаете к чему я клоню.
Намного быстрее читать из объектов, содержащих все необходимое здесь и сейчас. И намного проще вносить изменения в неструктурированную схему данных.
К тому же такие структуры данных проще делить, разносить по разным физическим хранилищам, распределять, ведь объекты уже содержат все необходимое.
Эти объекты мы можем воспринимать как отдельные страницы, файлы, карточки, все это можно назвать некими документами. Поэтому такая модель хранения данных называется документоориентированной.
Elasticsearch это документоориентированная база данных
Поиск
Теперь необходимо определиться с механизмами поиска. Данные организованы в виде документов. Как мы привыкли осуществлять поиск по документу?
Типичным примером документа будет веб-страница. Если мы попытаемся поискать по всей странице в браузере, поиск будет осуществляться по всему содержащемуся тексту. И это удобно для большинства кейсов.
Примерно так же работают многие поисковые системы, поиск происходит по всему тексту проиндексированных страниц, а не по отдельным полям, тегам или заголовкам. Это называется полнотекстовым поиском.
Искать предстоит по огромному количеству документов и было бы разумно запомнить что в каком документе лежит. В реляционных СУБД мы привыкли оптимизировать подобный поиск индексами.
Что такое индекс на самом деле? Если не вдаваться в детали, индекс это сбалансированное дерево, то есть дерево, в котором длина путей(количество шагов межу узлами) не будет отличаться больше чем на один шаг.
Например если бы мы проиндексировали наш пост, то у нас бы получилось дерево, листьями которого, являлись бы используемые в нем слова. Простыми словами, мы будем знать заранее, какие слова находятся в документе и как их быстро в нем найти. Не смотря на такую удобную структуризацию данных, обход дерева звучит как не самое лучшее решение для максимально быстрого поиска.
А что если сделать все наоборот — собрать список всех используемых слов и узнать, в каких документах они встречаются. Да, индексация займет больше времени, но нас в первую очередь интересует именно скорость поиска, а не индексации.
Такой индекс называется обратным индексом и используется для полнотекстового поиска.
Хороший пример — популярная open-source библиотека полнотекстового поиска, конечно же, с обратным индексом, Apache Lucene.
Elasticsearch использует индексы Lucene для хранения данных и поиска
Масштабирование
Как бы мы не пытались оптимизировать структуры данных и алгоритмы поиска, когда речь заходит о действительно больших массивах данных и действительно большом количестве запросов, необходимо задуматься о возможности повлиять на производительность системы путем увеличения аппаратного ресурса. Проще говоря, мы хотим иметь возможность накинуть немного памяти, ЦП и дискового пространства, чтобы все ехало быстрее. Мы можем назвать это масштабируемостью.
Самый простой вариант — накинуть железа на сервер. Если представить каждую условную единицу вычислительной мощности как деревянный кубик, то сейчас мы сложим кубики в одно место или один на другой, строя башню вертикально. Такое масштабирование и называется вертикальным.
Второй вариант — разделить наши задачи на группу машин. В этом случае мы тоже увеличиваем аппаратный ресурс, но сейчас кубики мы можем расположить на воображаемом столе как угодно на его плоскости, то есть горизонтально. Угадайте, как называется такое масштабирование?
Первый способ гарантирует нам быстрый результат без боли, но конечно не все так гладко. Как долго мы сможем увеличивать ресурс отдельной машины? Во-первых дешевым способом это будет только в самом начале, дальше оплата одного сервера будет стоить как несколько машин попроще. Во-вторых вы рано или поздно упретесь в потолок — железо, драйверы, пропускная способность и еще куча логических и физических ограничений. А самое главное, критический сбой в одной машине повлечет сбой всей системы, закономерно.
В отличии от первого способа второй не накладывает таких явных ограничений, мы можем добавлять машины сколько угодно, связывая их сетью. Конечно, это повлечет сетевые издержки — низкая скорость передачи в сети(в сравнении с обработкой на одной машине), сетевой оверхед. Но вместе с тем сеть имеет одно очень важное свойство — большую отказоустойчивость.
Распределенный индекс
Ок, для хранения данных и поиска мы будем использовать инстанс Lucene. Но ранее мы решили, что для обеспечения горизонтального масштабирования нам необходимо иметь возможность размещать данные на разных машинах. В действительности, какая разница как данные хранятся физически? Важно чтобы мы имели единое логическое хранилище. Каждый инстанс Lucene должен стать частью одного большого индекса, или осколком(shard) разбитого индекса. Шард будет выполнять непосредственно операции по поиску и записи данных.
Shard в Elasticsearch — это логическая единица хранения данных на уровне базы, которая является отдельным экземпляром Lucene.
Index — это одновременно и распределенная база и механизм управления и организации данных, это именно логическое пространство. Индекс содержит один или более шардов, их совокупность и является хранилищем.
Классическое сравнение индекса с другими базами выглядит примерно так.
Elasticsearch | SQL | MongoDB |
---|---|---|
Index | Database | Database |
Mapping/Type | Table | Collection |
Field | Column | Field |
Object(JSON) | Tuple | Object(BSON) |
Но существуют отличия в использовании этих абстракций. Рассмотрим еще один классический пример. У пользователя системы может храниться очень много информации, и мы решаем создавать новую базу для каждого пользователя. Это звучит дико! Но на самом деле в Elasticsearch это распространенная и даже хорошая практика. Индекс это довольно легкий механизм и лучше разделять большие данные, тем более, когда это логически оправдано. Системе проще работать с небольшими индексами чем с разросшейся базой для всего. Например, так вы можете создавать отдельный индекс для логов на каждый день и это широко используется.
По умолчанию количество шардов для индекса будет равным 5, но его всегда возможно изменить в настройках index.number_of_shards: 1 или с помощью запроса шаблонов индекса.
Важно управлять этим значением. Всегда принимайте решения с точки зрения параллельной обработки.
Каждый шард способен хранить примерно 2 32 или 4294967296 записей, это значит, что скорее всего вы упретесь в лимит вашего диска. Однако стоит понимать, все шарды будут участвовать в поиске и если мы будем искать по сотне пустых, потратим время впустую. Если шарды будут слишком большими мы так же будем тратить лишнее время на поиск, а так же операций перемещения и индексации станут очень тяжелыми.
Забегая вперед. Со временем Elasticsearch двигает и изменяет шарды, объединяя дробные и мелкие в большие. Следите за размером ваших шардов, при достижении 10ГБ производительность значительно падает.
Кластер
Мы определились с базовой концепцией распределенного индекса. Сейчас необходимо решить, как в действительности будет осуществляться управление отдельными базами.
Ранее мы решили, что за операции поиска и индексации отвечает отдельный инстанс Lucene(шард). Для того, чтобы обращаться к распределенной системе шардов, нам необходимо иметь некий координирующий узел, именно он будет принимать запросы и давать задания на запись или получение данных. То есть помимо хранения данных мы выделяем еще один вариант поведения программы — координирование.
Таким образом мы изначально ориентируемся на два вида узлов — CRUD-узлы и координирующие узлы. Назовем их data node и coordinating node. У нас есть куча машин объединенных в сеть и все это очень напоминает кластер.
Каждый запущенный экземпляр Elasticsearch является отдельным узлом(node). Cluster — это совокупность определенных нод. Когда вы запускаете один экземпляр ваш кластер будет состоять из одной ноды.
Конфигурация читается из файла elasticsearch.yml и переменных среды. Здесь мы можете настроить почти все, что касается неизменных в рантайме свойств ноды.
Каждый тип ответственности узлов налагает определенные системные требования. Очевидно, что data-ноды будут часто обращаться к диску и использовать значительные объемы памяти в процессе работы.
Мы так же можем утверждать, что не все данные будут запрашиваться одинаково часто. Данные постепенно «остывают» по мере снижения запросов. Мы можем назвать это жизненным циклом хранения данных. Хорошей идеей было бы держать хайповые публикации там, откуда их можно быстро достать, а забытые мемы 2007 можно положить подальше.
Начиная с версии 6.7 Elasticsearch предлагает механизм управления жизненным циклом. Для этого доступны три типа нод — hot, warm и cold.
Важнейшим аспектом в использовании распределенных систем является параллельное выполнение задач. Существует популярная модель распределенных вычислений, которая имеет лаконичное название MapReduce. И заключается она разделении выполнения задачи на два больших шага:
Именно такой механизм поможет нам выполнять операции с шардами. Координирующий узел получит запрос, предварительно переформулирует его для внутрикластерного взаимодействия и выполнит запросы к нашим worker-нодам(в данном случае к data-нодам).
Следовательно, coordinating-ноды должны иметь достаточный ресурс памяти, ЦП и быструю сеть, но при этом могут иметь скромный диск, ведь не осуществляют хранения данных.
Однако при большой частоте запросов, такие ноды могут стать узким местом системы. Мы можем пойти привычным путем и превратить точку внешнего доступа в плоскость. Пусть координирующих нод будет множество.
Такой подход позволит нам применять балансировку запросов, это можно сделать прямо в клиентском коде либо использовать любые существующие балансировщики.
Управление кластером
На данном этапе мы имеем возможность доступа к данным из множества точек — coordinating-нод. В этом нет проблем, если мы говорим о простых операциях чтения/записи в существующий индекс. Но если говорить о выделении новых шардов или их перемещении, может начаться путаница.
Предположим, возможность coordinating-нодам управления состоянием кластера. Один узел примет решение о перемещении шарда на одну data-ноду, а второй о перемещении того же на другую. Список возможных общекластерных действий может быть довольно широким, а список возможных конфликтов еще шире.
Очевидно такие важные решения должен принимать один центральный узел. Мы определили, что для каждого типа действий необходимо выделять отдельную роль, чтобы избежать потерь производительности на ноде. И «главный в кластере» звучит как отдельная ответственность.
Назовем такие ноды master-node. Активный мастер всегда должен быть один, он будет управлять топологией кластера: создавать новый индекс, выделять и распределять шарды, перемещать их и объединять в случае необходимости. Мастер всегда знает все о состоянии кластера.
Master-ноды отвечают за важные, но довольно легкие общекластерные действия. Это означает, что они требуют большого ресурса и высокой стабильности от физической ноды. В кластерах от 10 нод необходимо всегда выделять отдельные only-master узлы.
Репликация данных
Сейчас каждая запись в нашем индексе существует только в одном месте, и потеря хранящего ее узла приведет к потере данных на неопределенный срок. Для того, чтобы этого избежать существует механизм репликации. Важно не путать понятия реплики и бэкапа, если бэкап позволяет восстановить данные в случае утери, то реплика является полной копией базы.
Если мы потеряем одну из data-нод, то всегда сможем продолжить работу с репликами шардов в другом узле и тем временем вернуть потерянный.
То есть для каждого шарда должна быть как минимум одна копия на другой ноде. Можно конечно выделять по отдельной машине для каждой реплики, но это очень расточительно. Нужно разместить копии данных на разных узлах, но это не значит, что эти узлы должны хранить только шарды реплик.
Таким образом мы всегда имеем реплики всех шардов и не поднимаем неэффективно простаивающие ноды.
Основной шард назовем первичным или primary shard, а любую из его копий реплицирующим шардом или replica shard, первичный шард и его реплики это группа репликации.
С учетом реплик запись данных будет происходить в два этапа, в первом запись затронет только первичный шард и только после того, как произойдет операция flush слияния изменений и операция commit фиксации в индексе Lucene, будет отправлен внутренний запрос на изменение всех реплик.
Для максимальной стабильности кластера необходимо, чтобы количество дата-нод было больше или равно количества реплик.
Отказоустойчивость
Сейчас данные будут доступны даже в случае сбоя одного из хранящих узлов. Но что если кластер потеряет мастера? Потеря единственного мастера равноценна потере кластера.
Тут все по привычной схеме — поднимаем несколько мастеров.
Но если у нас есть, например, два управляющих узла, как понять, какой из них в данный момент должен управлять кластером? Как они смогут договориться о своих решениях? Очевидно, что в каждый момент времени должен быть только один управляющий кластером узел.
То есть при потере мастера его место должен занять один из кандидатов.
Представим. Главный управляющий узел стал недоступен для кластера, кластер берет первого кандидата и устанавливает его на вакантное место. Спустя определенное время первый мастер возвращается в кластер и ничего не знает о том, что его место уже занято. Мастер-ноды являются своего рода его мозгом, и теперь мозг кластера становится разделен. Это классическая проблема распределенных систем и она так и называется split-brain problem.
В обществе подобные проблемы зачастую решаются путем голосования. Подобный механизм используется и в распределенных системах. Как только кластер теряет управляющий узел, должен быть запущен процесс голосования.
Важно определить какой из кандидатов больше всего подходит на роль главного узла. Такой кандидат должен обладать самой актуально информацией о кластере. Для краткого описания актуальности информации о кластере может использоваться версионирование. При каждом изменении кластера главный узел будет обновлять некую служебную информацию и повышать номер версии, далее то же самое будет параллельно происходить в нодах-кандидатах.
Сравнив номера версий мы можем определить наиболее подходящих кандидатов на роль мастера. Теперь если отпавшая мастер-нода вернется в кластер, то процесс голосования запустится снова и будет выбран единственный управляющий узел.
Теперь важно понять когда можно считать, что голосование прошло успешно? Если проголосовали все участники? Или половина? Или другое любое другое магическое количество?
Решение этой проблемы заключается в определении кворума. Это умное название для контрольного количества голосующих.
Очевидно, что такое важное решение как выбор мастера должно приниматься на основе большинства, то есть 50%+один голос. Справедливо, надежно. Это значение и станет кворумом.
Таким образом, количество кандидатов на мастера должно быть нечетным и не меньше трех. Рекомендуется использовать простую формулу для расчета оптимально количества таких нод:
КОЛИЧЕСТВО_КАНДИДАТОВ = ОБЩЕЕ_КОЛИЧЕСТВО_НОД/2 + 1
Решения для любых общекластерных действий принимаются путем голосования, и вся необходимая для голосования информация содержится в конфигурации голосования. Право голоса определяет еще одну роль, ведь право голоса не означает, что узел может быть кандидатом.
Elasticsearch автоматически изменяет конфигурацию голосования при изменении кластера. Поэтому нельзя одновременно отключать половину или более голосующих нод. Например, если в вашей конфигурации в данный момент 7 голосующих нод и вы отключили сразу 4, кластер станет недоступным, потому что останется 3 ноды, а в конфигурации голосования кворумом является значение 4.
Теперь, если кластер разделится на две части, узлы меньшей, пропинговав доступные в ней узлы и сравнив их количество со значением кворума, будут знать, что именно они отпали от кластера и не могут участвовать в принятии решений.
Транспорт
Пришло время поговорить о том, как общаться с кластером из внешних систем, и как будут общаться узлы внутри кластера. Есть ряд плюсов и минусов использования и традиционных, и специальных протоколов. Для краткого сравнения существует таблица.
Протокол | Достоинства | Недостатки |
---|---|---|
HTTP | Низкий порог вхождения, в сравнении с нативным протоколом. Для использования нужен только HTTP клиент и погнали. HTTP API никогда не ломает совместимость, при обновлении версии ES, ваше приложение продолжит работать так же. Возможно проксировать и использовать балансировщики нагрузки. JSON. | Клиент не знает топологию кластера, поэтому может потребовать большее количество запросов для получения данных. Оверхед. |
ES Native | Лучший выбор для ОЧЕНЬ больших данных. Если необходимо выполнить большое количество операций с индексом, нативный протокол значительно ускорит. | Используется под JVM. Использование влечет жесткую связность с ES. Обновления требуют перекомпиляции и повторного развертывания пользовательских клиентов. Возможны обновления ломающие совместимость. |
Для внутренней коммуникации в кластере Elsaticsearch использует нативный протокол.
Заключение
Хочется верить, что прочитав эту статью вы поняли основы распределенных поисковых систем. Возможности масштабирования и отказоустойчивости — это то для чего был создан Elasticsearch и то, почему он приобрел популярность.
Я постарался кратко и последовательно рассказать о том, как и почему именно так это устроено. В этой статье я намеренно не стал упоминать об экосистеме Elastic, плагинах, запросах, токенизации, маппинге и остальном. Так же я не сказал об Ingest и machine learning нодах, на мой взгляд, они дают дополнительные возможности и не являются базовыми.
Дополнительные материалы
Книга Elasticsearch 5.x Cookbook, Third Edition