Elasticsearch Heap: Размер и Свопинг

При установке Elasticsearch по умолчанию используется куча размером 1 ГБ. Для практически любого развертывания это число обычно слишком мало. Если вы используете значения кучи по умолчанию, вероятно, ваш кластер настроен неправильно.

Размер и Свопинг

Существует два способа изменить размер кучи в Elasticsearch. Самый простой - установить переменную окружения под названием ES_HEAP_SIZE. Когда серверный процесс запускается, он считывает эту переменную окружения и устанавливает размер кучи соответствующим образом. В качестве примера, вы можете установить ее через командную строку следующим образом:

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

Убедитесь, что минимальный (Xms) и максимальный (Xmx) размеры одинаковы, чтобы предотвратить изменение размера кучи во время выполнения, что является очень дорогостоящим процессом.

Как правило, установка переменной среды ES_HEAP_SIZE предпочтительнее, чем установка явных значений -Xmx и -Xms.

Отдайте (менее) половины своей памяти Lucene

Частой проблемой является настройка слишком большой кучи. У вас машина с 64 ГБ памяти, и вы хотите отдать Elasticsearch все 64 ГБ памяти. Больше - значит лучше!

Куча определенно важна для Elasticsearch. Она используется многими структурами данных в памяти для обеспечения быстрой работы. Но, несмотря на это, есть еще один крупный пользователь памяти, который находится вне кучи: Lucene.

Lucene разработан для использования возможностей базовой ОС для кэширования структур данных в памяти. Сегменты Lucene хранятся в отдельных файлах. Поскольку сегменты неизменяемы, эти файлы никогда не меняются. Это делает их очень удобными для кэширования, и базовая ОС с радостью сохранит горячие сегменты в памяти для более быстрого доступа. Эти сегменты включают как инвертированный индекс (для полнотекстового поиска), так и значения doc (для агрегации).

Производительность Lucene зависит от этого взаимодействия с ОС. Но если отдать всю доступную память под кучу Elasticsearch, то для Lucene ничего не останется. Это может серьезно повлиять на производительность.

Стандартная рекомендация - отдать 50% доступной памяти под кучу Elasticsearch, а остальные 50% оставить свободными. Она не останется неиспользованной; Lucene с радостью поглотит все, что останется.

Если вы не будете агрегировать анализируемые строковые поля (например, вам не понадобятся полевые данные), вы можете уменьшить объем кучи еще больше. Чем меньше куча, тем лучшей производительности вы можете ожидать как от Elasticsearch (более быстрый GC), так и от Lucene (больше памяти для кэширования).

Не превышайте 32 ГБ!

Есть еще одна причина не выделять огромные кучи для Elasticsearch. Как выяснилось, HotSpot JVM использует "трюк" для сжатия указателей объектов, когда куча не превышает 32 ГБ.

В Java все объекты выделяются в куче и на них ссылается указатель. Обычные объектные указатели (OOP) указывают на эти объекты и традиционно имеют размер собственного процессора: либо 32 бита, либо 64 бита, в зависимости от процессора. Указатель ссылается на точное расположение байта значения.

Для 32-битных систем это означает, что максимальный размер кучи составляет 4 ГБ. Для 64-битных систем размер кучи может быть намного больше, но накладные расходы 64-битных указателей означают, что будет больше пустого пространства просто потому, что указатель больше. И что еще хуже, большие указатели съедают больше пропускной способности при перемещении значений между основной памятью и различными кэшами (LLC, L1 и т.д.).

Чтобы обойти эту проблему, Java использует трюк, называемый компрессия oops. Вместо того чтобы указывать на точное расположение байтов в памяти, указатели ссылаются на смещения объектов. Это означает, что 32-битный указатель может ссылаться на четыре миллиарда объектов, а не на четыре миллиарда байт. В конечном счете, это означает, что куча может вырасти примерно до 32 ГБ физического размера при использовании 32-битных указателей.

Как только вы пересекаете эту магическую границу в ~32 ГБ, указатели снова превращаются в обычные указатели объектов. Размер каждого указателя растет, используется большая пропускная способность процессора-памяти, и вы фактически теряете память. Фактически, требуется около 40-50 ГБ выделенной кучи, прежде чем у вас будет столько же эффективной памяти, сколько у кучи размером чуть меньше 32 ГБ при использовании сжатого oops.

Мораль этой истории такова: даже если у вас есть свободная память, старайтесь не пересекать границу кучи в 32 ГБ. Это тратит память, снижает производительность процессора и заставляет GC бороться с большими кучами.

Насколько меньше 32 Гб нужно установить JVM?

К сожалению, это зависит от ситуации. Точное ограничение варьируется в зависимости от JVM и платформы. Если вы хотите перестраховаться, то, скорее всего, будет безопасно установить кучу на 31gb. В качестве альтернативы, вы можете проверить точку отсечения для HotSpot JVM, добавив -XX:+PrintFlagsFinal в опции JVM и проверив, что значение флага UseCompressedOops равно true. Это позволит вам найти точное значение отсечки для вашей платформы и JVM.

Например, здесь мы тестируем установку Java 1.7 на MacOSX и видим, что максимальный размер кучи составляет около 32600мб (~31.83гб) до отключения сжатых указателей:

Для сравнения, максимальный размер кучи Java 1.8 на той же машине составляет около 32766мб (~31,99гб):

Мораль этой истории такова: точное значение для использования сжатых OOP варьируется от JVM к JVM, поэтому будьте осторожны, беря примеры из других источников, и обязательно проверьте свою систему с вашей конфигурацией и JVM.

Начиная с версии Elasticsearch v2.2.0, журнал запуска действительно скажет вам, использует ли ваша JVM сжатые OOPs или нет. Вы увидите в журнале сообщение вида:

Это указывает на то, что используются сжатые указатели объектов. Если это не так, в сообщении будет написано [false].
У меня машина с 1 ТБ оперативной памяти!

Строка 32 ГБ довольно важна. Что же делать, если у вашей машины много памяти? Все чаще можно встретить суперсерверы с 512-768 ГБ оперативной памяти.

Во-первых, мы бы рекомендовали избегать таких больших машин.

Но если у вас уже есть такие машины, у вас есть три практических варианта:

Вы занимаетесь в основном полнотекстовым поиском? Подумайте о том, чтобы отдать 4-32 ГБ Elasticsearch и позволить Lucene использовать остальную память через кэш файловой системы ОС. Вся эта память будет кэшировать сегменты и обеспечит молниеносную скорость полнотекстового поиска.

Много ли вы делаете сортировок/агрегаций? Большинство ваших агрегаций - это числовые значения, даты, гео-точки и не_анализируемые строки? Вам повезло, ваши агрегации будут выполняться на удобных для памяти значениях doc! Выделите Elasticsearch от 4 до 32 ГБ памяти, а остальное оставьте для ОС, чтобы она кэшировала значения документов в памяти.

Вы делаете много сортировок/агрегаций на анализируемых строках (например, для слов-тегов, или SigTerms, и т.д.)? К сожалению, это означает, что вам понадобятся данные полей , а это означает, что вам нужно место в куче. Вместо одного узла с огромным объемом оперативной памяти рассмотрите возможность запуска двух или более узлов на одной машине. При этом все равно придерживайтесь правила 50%.

Так, если ваша машина имеет 128 ГБ оперативной памяти, запустите два узла, каждый из которых имеет чуть меньше 32 ГБ. Это означает, что для кучи будет использовано менее 64 ГБ, а для Lucene останется более 64 ГБ.

Если вы выбрали этот вариант, установите в конфигурации cluster.routing.allocation.same_shard.host: true. Это предотвратит размещение основного и копийного шардов на одной физической машине (поскольку это лишит преимущества высокой доступности реплик).

Свопинг - это смерть производительности

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

Если память переключается на диск, операция длительностью 100 микросекунд превращается в операцию длительностью 10 миллисекунд. Теперь повторите это увеличение латентности для всех остальных 10-секундных операций. Нетрудно понять, почему свопинг отрицательно сказывается на производительности.

Лучше всего полностью отключить своппинг в вашей системе. Это можно сделать временно:

Чтобы отключить его навсегда, вам, скорее всего, придется отредактировать файл /etc/fstab. Обратитесь к документации по вашей ОС.

Если отключить своп полностью не представляется возможным, вы можете попробовать снизить уровень свопинга. Это значение контролирует, насколько агрессивно ОС пытается обменять память. Это предотвращает свопинг при нормальных обстоятельствах, но позволяет ОС выполнять свопинг в аварийных ситуациях с памятью.

Для большинства систем Linux это значение настраивается с помощью параметра sysctl:

Значение swappiness 1 лучше, чем 0, так как в некоторых версиях ядра swappiness 0 может вызвать OOM-killer.

Наконец, если ни один из подходов невозможен, вам следует включить mlockall. file. Это позволит JVM заблокировать свою память и предотвратить ее подкачку со стороны ОС. В вашем elasticsearch.yml установите следующее:

Понравилась статья? Поделиться с друзьями:
Добавить комментарий