- PVSM.RU - https://www.pvsm.ru -

Масштабируем Elasticsearch на примере кластера с индексами в несколько терабайт

Низкая скорость поисковых запросов

Работая над поисковым движком по социальной информации (ark.com [1]), мы остановили свой выбор на Elasticsearch, так как по отзывам он был очень легок в настройке и использовании, имел отличные поисковые возможности и, в целом, выглядел как манна небесная. Так оно и было до тех пор, пока наш индекс не вырос до более-менее приличных размером ~ 1 миллиарда документов, размер с учетом реплик уже перевалил за 1,5 ТБ.

Даже банальный Term query мог занять десятки секунд. Документации по ES не так много, как хотелось бы, а гуглинг данного вопроса выдавал результаты 2х-летней давности по совсем не актуальным версиям нашего поискового движка (мы работаем с 0.90.13 — что тоже не достаточно старая вещь, но мы не можем позволить себе опустить весь кластер, обновить его, и запустить заново на текущий момент — только роллинг рестарты).

Низкая скорость индексации

Вторая проблема — мы индексируем больше документов в секунду (порядка 100к), чем Elasticsearch может обрабатывать. Тайм-ауты, огромная нагрузка на Write IO, очереди из процессов в 400 единиц. Все выглядит очень страшно, когда смотришь на это в Marvel.

Как решать эти проблемы — под катом

Масштабируем кластер Elasticsearch

Исходная ситуация:

  • 5 data nodes, http enabled:
    • 100 GB RAM
    • 16 cores
    • 4 TB HDD (7200 RPM, seagate)

  • Индексы:
    • от 500 до 1 млрд документов, всего 5 штук
    • количество primary шардов от 50 до 400 (здесь мы тестировали разные стратегии индексирования — эта настройка очень важна)
    • реплики — от 2 до 5
    • размер индекса до 1,5 терабайт

Увеличиваем скорость индексирования в Elasticsearch

Эта проблема оказалось не такой сложной и информации в интернете по ней чуть больше.

Чеклист, который нужно проверить:

  • refresh_interval — как часто обновляются данные для поиска, чем чаще, тем больше Write IO вам требуется
  • index.translog.flush_threshold_ops — через сколько операций скидывать данные на диск
  • index.translog.flush_threshold_size — сколько данных должны быть добавлены в индекс перед скидыванием на диск

Подробная документация здесь: www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-update-settings.html [2]

В первую очередь мы увеличили refresh_interval до 30 секунд, и фактически увеличили пропускную способность практически до 5000 документов в секунду. Позже поставили flush_threshold_ops в 5000 операций, а размер до 500 мб. Если хотите, то можно поиграться с количеством реплик, шардов и так далее, но это не будет давать настолько большой разницы. Так же обратите внимание на threadpool, если вам необходимо увеличить количество параллельных запросов к базе, хотя чаще всего этого не требуется.

Увеличиваем скорость запросов в Elasticsearch

Теперь переходим к сложной части. Зная размер нашего индекса и постоянные потребности в перезагрузке кластера (обновления версий, мейнтенанс машин), а также принимая во внимание посты вроде этого: gibrown.wordpress.com/2014/02/06/scaling-elasticsearch-part-2-indexing/ [3] мы решили, что размер шарда в нашем индексе не будет превышать 1-2 ГБ. С учетом RF3, наш индекс (мы рассчитываем на 1,5 млрд документов), учитывая что 0,5 млрд наших документов занимают порядка 300 ГБ без учета реплик, мы создали в индексе 400 шардов и посчитали что все будет хорошо — скорость ребута будет достаточно высока: нам не нужно будет читать блоки данных по 50-60 ГБ, а также реплицировать их, блокируя таким образом восстановление маленьких шардов, да и скорость поиска по маленьким шардам выше.

По началу, количество документов в индексе было небольшим (100-200 млн) и скорость запроса составляла всего 100-200 мс. Но как только практически все шарды были заполнены хотя бы небольшим количеством документов, мы начали значительно терять в производительности запросов. Комбинируя все это с высокой нагрузкой на IO из-за постоянной индексации, мы могли и вообще не выполнить его.

В данном случае мы совершили 2 ошибки:

1. Создали очень много шардов (идеальная ситуация 1 ядро — 1 шард)
2. Наши дата ноды были и нодами-балансерами с включенным http — сериализация и десериализация данных занимает достаточно много времени

Поэтому мы начали экспериментировать.

Добавялем ноды-балансировщики в Elaticsearch

Первым и очевидным шагом для нас было добавлением, так называемых, balancer nodes в Elasticsearch. Они могут производить агрегированние результатов запросов по другим шардам, у них никогда не будет перегружен IO, так как они не выполняют чтения и записи на диск, и мы разгрузим наши data nodes.

Для деплоя мы используем chef и соответствующий elasticsearch cookbook, поэтому создав всего пару дополнительных ролей, со следующими настройками:

name "elasticsearch-balancer"
description "Installs and launches elasticsearch"

default_attributes(
	"elasticsearch" => {
		"node" => {
			"master" => false,
			"data" => false
		}
	}
)

run_list("services::elasticsearch")

Мы благополучно запустили 4 балансировщика. Картина немного улучшилась — мы больше не наблюдали перегруженных нод с дымящимися жесткими дисками, но скорость запросов была все еще низка.

Увеличиваем количество data nodes в Elasticsearch

Теперь мы вспомнили, что количество шардов, которое было у нас (400) никоим образом не сказывается на улучшении производительности, а лишь усугубляет ее, так как слишком больше количество шардов находится на 1 машине. Проведя простые вычисления мы получаем, что 5 машин адекватно поддержат только 80 шардов. Учитывая количество реплик, то их у нас вообще 1200.

Так как наш общий парк машин (80 нод) позволяет добавление достаточно большого количества нод и основная проблема в них — это размер HDD (всего 128гб), то мы решили добавить сразу порядка 15 машин. Это позволит работать с еще 240 шардами более эффективно.

Помимо этого мы наткнулись на несколько любопытных настроек:

* index.store.type — по умолчанию ставится в niofs, а по бенчмаркам производительность ниже чем у mmapfs — мы переключили его на mmapfs (дефолтный стор в 1.x)
* indices.memory.index_buffer_size — увеличили до 30%, а количество RAM под Java Heap наоборот уменьшили до 30 ГБ (было 50%), так как с mmapfs нам нужно намного больше оперативки для кеша операционной системы

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

curl -XPUT localhost:9200/_cluster/settings -d '{
    "transient" : {
        "cluster.routing.allocation.disk.threshold_enabled" : true
    }
}'

После пары дней переноса шардов и перезапуска старых серверов с новыми настройками, мы провели тесты и не кешированные запросы (Term Query, не фильтры) выполнялись не более 500 мс. Данная ситуация все еще не идеальна, но мы видим, что добавление дата нод и подгон количества ядер под количество шардов исправляет ситуацию.

Что еще следует учесть при масштабировании кластера

При роллинг рестарте кластера, обязательно выключайте возможность переноса шардов: cluster.routing.allocation.enable = none, в старых версиях чуть другая настройка.

Если возникли вопросы во время прочтения — буду рад обсудить.

Автор:

Источник [4]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/servernoe-administrirovanie/61279

Ссылки в тексте:

[1] ark.com: https://ark.com

[2] www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-update-settings.html: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-update-settings.html

[3] gibrown.wordpress.com/2014/02/06/scaling-elasticsearch-part-2-indexing/: http://gibrown.wordpress.com/2014/02/06/scaling-elasticsearch-part-2-indexing/

[4] Источник: http://habrahabr.ru/post/224877/