Публикуем стенограмму видеозапись выступления Алексея Гончарука (Член Apache Ignite PMC и главный архитектор GridGain) на Встреча сообщества Apache Ignite в Санкт-Петербурге 29 марта.
Скачать слайды можно связь .
Членов сообщества Apache Ignite часто спрашивают: «Сколько узлов и памяти требуется для загрузки такого объема данныхЭ» Именно об этом я хочу поговорить сегодня.
Забегая вперед: такое прогнозирование пока остается достаточно сложной, нетривиальной задачей.
Для этого нужно немного разобраться в устройстве Apache Ignite. Я также расскажу вам, как упростить вашу задачу прогнозирования и какие оптимизации вы можете применить.
Итак, очень часто к нам приходят пользователи и говорят: «У нас есть данные, представленные в виде файлов.
Сколько памяти требуется для перевода этих данных в Apache Ignite? С такой формулировкой ответить на вопрос практически невозможно, поскольку разные форматы файлов трансформируются в совершенно разные модели.
Например, файл может быть сжат. А если он не сжимается в какую-то классическую бинарную форму, но может дедуплицировать данные, то файл сжимается в неявном виде.
Также не стоит забывать, что Apache Ignite обеспечивает быстрый доступ к данным с помощью различных ключей или SQL-индексов, поэтому помимо данных, которые находятся в файле, мы строим дополнительные индексы, что связано с дополнительными затратами.
Таким образом, в целом неверно утверждать, что один индекс добавит некоторый процент от общего объема данных, поскольку индексируемое поле может иметь разные размеры.
То есть неправильно выделять под индекс фиксированный процент памяти.
Переформулируем задачу.
Мы говорим, что наша модель состоит из нескольких типов, и мы хорошо понимаем структуру каждого типа.
Более того, если в модели есть поля переменного размера, скажем, строки, то мы можем примерно оценить минимальный и максимальный размеры данных, а также распределение размеров во всем наборе данных.
Исходя из того, сколько типов данных у нас есть и сколько информации имеется для каждого типа, мы будем планировать объем памяти и дисков.
эмпирический подход
Эмпирический подход может быть немного неточным, но, с точки зрения пользователя, он не требует какого-либо глубокого погружения в структуру системы.Суть подхода заключается в следующем.
Возьмите репрезентативную выборку данных и загрузите ее в Apache Ignite. Во время загрузки наблюдайте за увеличением размера хранилища.
При определенных объемах можно делать «обрезки», чтобы затем линейно экстраполировать и спрогнозировать необходимый объем хранения для всего набора данных.
Здесь часто задают хороший вопрос: «Какой должна быть репрезентативная выборкаЭ» Однажды мы случайным образом сгенерировали выборку.
Но оказалось, что генератор собирал данные таким образом, что в набор при генерации попадали остатки деления.
В какой-то момент выяснилось, что наши случайные строки, которые должны были иметь равномерное распределение, на самом деле этого распределения не имели.
Поэтому, когда вы оцениваете емкость хранилища, убедитесь, что репрезентативная выборка действительно соответствует дистрибутивам, с которыми вы работаете.
Также обратите внимание на изменение размеров ваших объектов.
Если вы ожидаете, что они будут одинаковой длины, то, как правило, для репрезентативной выборки достаточно небольшого количества объектов.
Чем больше изменчивость, чем больше комбинаций размеров полей, тем больше выборка, которую необходимо будет загрузить, чтобы понять взаимосвязь.
По своему опыту скажу, что зависимость начинает проявляться примерно при миллионе объектов, загружаемых в одну партицию на узел.
На что именно нужно обратить внимание при загрузке репрезентативной выборки? Если вы работаете с постоянством, вы можете посмотреть объем получаемых файлов.
А можно просто включить метрики данных региона, метрики в конфигурации Apache Ignite и отслеживать прирост памяти через MX Bean, делая «отсечения» и строя график.
Численная оценка
В этом случае мы пройдем все этапы изменения структуры данных, которым подвергается ваш объект при сохранении в Apache Ignite, а также посмотрим на окружающие структуры данных, которые могут увеличить потребление памяти.Как только мы поймем, какие изменения происходят и какие структуры данных изменяются, мы сможем довольно точно оценить объем памяти, необходимый для загрузки данных.
Давайте проанализируем операцию вывода денег при написании в Apache Ignite. Данные проходят 4 стадии трансформации:
Первый этап не является обязательным, поскольку некоторые пользователи работают с классом и передают объект Java в Apache Ignite. Некоторые пользователи создают двоичный объект напрямую, поэтому первый этап преобразования объекта пропускается.
Но если вы работаете с объектами Java, то это первое преобразование, которому подвергается объект. После преобразования объекта Java в двоичный объект мы получаем независимый от класса формат. Его суть в том, что вы можете работать с бинарными объектами в кластере, не имея описания класса.
Это позволяет изменять структуру класса и выполнять так называемые скользящие изменения структуры объектов.
То есть ваша модель растет, меняется, и вы получаете возможность работать со своими данными, не меняя классов, не развертывая их в кластере.
Третий этап изменений, вносящий дополнительные накладные расходы, — запись на диск.
Единицей работы диска традиционно является страница.
А начиная с Apache Ignite 2.0 мы перешли на страничную архитектуру.
Это означает, что каждая страница имеет необязательные заголовки, некоторые метаданные, которые также занимают место при записи объектов на страницу.
И последний момент, который также необходимо принять во внимание, — это обновление индекса.
Даже если вы не используете SQL, у вас есть быстрый доступ к ключам в Apache Ignite. Это основной API кэша Apache Ignite. Поэтому всегда строится индекс первичного ключа, и на него тоже тратится место.
Это наш двоичный объект:
Мы не будем слишком углубляться в структуру; в целом его можно представить как своего рода заголовок, затем поля, наши необходимые данные и нижний колонтитул.
Заголовок двоичного объекта имеет длину 24 байта.
Это может быть много, но необходимо поддерживать изменяемую природу объектов без классов.
Если модель данных, которые вы кэшируете, имеет какую-то свободную внутреннюю структуру, возможно, стоит посмотреть, сможете ли вы встроить несколько небольших объектов в исходный большой объект? В принципе, такой инлайнинг сэкономит вам 24 байта на объект, что при достаточно большой модели дает существенный прирост. Размер нижнего колонтитула зависит от флага CompactFooter, который позволяет записывать структуру объекта в дополнительные метаданные.
То есть вместо того, чтобы писать порядок полей в самом объекте, мы храним их отдельно.
А если CompactFooter имеет значение true, то нижний колонтитул будет очень маленьким.
Однако Apache Ignite предпринимает дополнительные шаги для сохранения и обслуживания этих метаданных.
Если CompactFooter имеет значение false, то объект самодостаточен и его структуру можно прочитать без дополнительных метаданных.
В настоящее время в нашем общедоступном API нет метода, возвращающего размер двоичного объекта.
Поэтому, если вас это очень интересует, вы можете сделать хак и довести объект до реализации, тогда вы увидите его размер.
Думаю, в Apache Ignite 2.5 мы добавим метод, который позволит нам получать размер объекта.
Архитектура страницы
Как я уже говорил, единицей работы диска является страница.Это сделано для оптимизации чтения и записи диска.
Но это накладывает ограничения на внутреннюю архитектуру Apache Ignite, поскольку любые структуры данных, которые будут сохранены на диске, должны работать и со страницами.
Другими словами, любая структура данных, будь то дерево или свободный список, строится из блоков страниц.
Страницы ссылаются друг на друга, используя уникальный идентификатор.
Используя тот же уникальный идентификатор, мы можем за константное время определить файл, из которого эти страницы можно читать, и с каким смещением из этого файла будет читаться та или иная страница.
Единый раздел, разбитый на множество страниц, можно представить в виде такой схемы:
Начальная мета-страница позволяет вам перейти на любую другую страницу.
Они разделены на разные типы, которых довольно много, но для упрощения примера скажу, что мы имеем:
- страницы данных, на которых хранятся данные;
- индексные страницы, позволяющие построить индексное дерево;
- вспомогательные страницы для таких структур, как свободный список или метаданные.
Давайте начнем с страницы данных .
Этот блок принимает наш ключ и значение, когда мы записываем данные в Apache Ignite. Сначала берется страница данных, которая может вместить нашу информацию, и данные в нее записываются.
При получении ссылки на них информация записывается в индекс.
Страница данных имеет табличную организацию.
В начале есть таблица, содержащая смещение пар ключ-значение.
Сами пары ключ-значение записываются в обратном порядке.
Самая первая запись расположена в конце страницы.
Это сделано для того, чтобы было удобнее работать со свободным пространством.
Вы можете спросить, почему это было так сложно? Почему вы не можете записывать данные непосредственно на страницу и ссылаться на них со смещением внутри страницы? Это сделано для дефрагментации страницы.
Если удалить здесь запись №2, то образуются две свободные зоны.
Возможно, в будущем запись №3 придется переместить вправо, чтобы разместить запись большего размера.
А если у нас косвенная адресация, то вы просто меняете смещение соответствующей записи в таблице.
Однако внешняя ссылка, ведущая на эту страницу, остается постоянной.
Строка может быть фрагментированной в том смысле, что она будет больше размера страницы.
В этом случае мы возьмем пустую страницу и запишем в нее хвост строки, а остальное — в страницу данных.
Помимо ключа и значения, в страницу данных записывается также вспомогательная информация для корректной работы системы, например, номер версии.
Если вы используете политику истечения срока действия, там также указывается время истечения срока действия.
Обычно дополнительные метаданные занимают 35 байт. Зная размер двоичного объекта и ключа, вы добавляете 35 байт и получаете размер конкретной записи на странице данных.
А потом вы подсчитываете, сколько записей поместится на странице.
Может случиться так, что на странице данных окажется свободное место, в которое не поместится ни одна из записей.
Тогда в метрике вы увидите коэффициент заполнения, не равный 1. И несколько слов о процедуре регистрации .
Допустим, у вас была пустая страница и вы записали на нее какие-то данные.
Осталось много свободного места.
Было бы неправильно просто выбросить страницу и оставить ее где-нибудь лежать и больше не использовать.
Информация о том, на каких страницах имеется свободное пространство, которая имеет смысл, хранится в структуре данных списка свободных мест. Если в этой реализации и на данный момент страница содержит менее 8 байт, она не попадает во свободный список, потому что не будет пары ключ-значение, которая умещалась бы в 8 байт. После того, как мы разобрались со страницами данных и оценили их количество, мы можем оценить количество индексные страницы , который нам понадобится.
Любой индекс Apache Ignite представляет собой B-дерево.
Это означает, что на самом низком — самом широком — уровне есть ссылки абсолютно на все пары ключ-значение.
Индекс начинается с корневой страницы.
Каждая из внутренних страниц имеет ссылки на уровень ниже.
Для индекса, являющегося первичным ключом, размер элемента, записываемого на страницу индекса, составляет 12 байт. В зависимости от того, просматриваете ли вы внутреннюю страницу или страницу листа, у вас будет разное максимальное количество элементов.
Если взяться за такую числовую оценку, то можно увидеть количество максимальных элементов в коде.
Для первичного индекса размер ссылки всегда фиксирован и равен 12 байтам.
Чтобы примерно подсчитать максимальное количество элементов страницы, можно разделить размер страницы (по умолчанию 4 мегабайта) на 12 байт. Учитывая рост дерева, можно предположить, что каждая страница будет заполнена от 50% до 75%, в зависимости от порядка загрузки данных.
Учитывая, что нижний уровень дерева содержит все элементы, вы также можете оценить количество страниц, необходимых для хранения индекса.
Что касается индексов SQL — или вторичных — размер элемента, хранящегося на странице, зависит от настроенного встроенного размера.
Вам необходимо очень тщательно проанализировать модель данных, чтобы рассчитать количество индексных страниц.
Много вопросов вызывает дополнительный расход памяти на один раздел.
Для запуска пустого кэша, в котором в каждом из разделов записан буквально по одному элементу, требуется значительный объем памяти.
Дело в том, что при такой структуре все необходимые метаданные должны быть инициализированы для каждого раздела.
Число разделов по умолчанию — 1024, поэтому, если вы запустите один узел и начнете записывать по одному элементу в каждый раздел, то вы сразу же инициализируете очень большое количество метастраниц, что приводит к таким большим начальным затратам памяти.
При эмпирическом подходе вы загрузите довольно большой объем данных, и потребление памяти разделом станет менее заметным.
Но стоит принять это во внимание для более точной оценки.
Если сложить затраты памяти на раздел, все страницы данных и страницы индекса, можно довольно точно рассчитать необходимый объем памяти.
Оптимизации
Как можно облегчить жизнь пользователю в будущем? Apache Ignite движется в сторону систем SQL, и существует множество идей по снижению накладных расходов.Может изменить двоичный формат чтобы уменьшить размер заголовка.
Если вы перейдете на более строго типизированную структуру данных в любом из кэшей, то перетасовать объекты не получится, но объем потребляемой памяти уменьшится.
Второе решение - группировать однотипные объекты на страницах и выделите заголовок или его часть.
В этом случае Apache Ignite самостоятельно дедуплицирует данные на уровне страницы.
Еще одна умная идея: реализовать собственный порог для учета данных в свободном списке .
Если вы понимаете, что страницы будут фрагментированы таким образом, что в них останется 100 байт, а ваши данные никогда не будут меньше 100 байт, то имеет смысл подправить фрилист, чтобы страницы туда не попадали и не тратились зря.
место в этом самом свободном списке.
Активно обсуждается и внедряется системой сжатие данных на уровне страницы , прозрачный для пользователя.
Есть некоторые технические трудности, но в целом вы будете жертвовать производительностью в пользу более компактного размещения данных.
И последняя, очень популярная оптимизация — калькулятор емкости кластера .
Большой вопрос в том, что будет входом для такой утилиты.
Выглядит это так: пользователь загружает в калькулятор структуру объектов, указывает, сколько строк он планирует загрузить, а калькулятор говорит, сколько памяти необходимо с учетом всех индексов и внутренних накладных расходов Apache Ignite.
Определение соотношения диск/память
Какой объем памяти нам нужно выделить, учитывая, что объем хранимых данных превышает объем доступной памяти? Если вам не хватает памяти, то Apache Ignite делает примерно то же самое, что и ОС: выкидывает часть данных из памяти и загружает нужные.
Некоторые данные нельзя выбросить, но для большинства случаев это не важно.
Здесь нужно помнить, что сам внешний вид страницы не предполагает записи.
Выбрасывать данные из памяти дешево.
А последующее чтение данных, по которым произошел промах, с диска — дорогостоящая операция.
В большинстве случаев наиболее важной статистикой, на которую следует обратить внимание, является количество операций ввода-вывода в секунду, которое может обеспечить ваш диск.
Если вы работаете с облачным развертыванием, то узнать количество IOPS очень легко.
Например, при запуске и монтировании образа в Amazon вы можете выбрать среди дисков те, которые имеют скорость записи в МБ/сек.
Но, по нашему опыту, гораздо полезнее знать IOPS. Поскольку мы работаем со страницами, по сути, IOPS — это максимальное количество операций чтения или записи, которые мы можем выполнить на диске в единицу времени.
Как выбирается страница, которую нужно удалить из памяти? Теперь это делается с помощью случайного алгоритма LRU. Apache Ignite содержит страницу в памяти, на которой хранится сопоставление того же идентификатора страницы с определенным физическим адресом в памяти, где расположены данные.
Когда нам нужно выбросить какую-либо из страниц, мы берем n случайных страниц из этой таблицы и выбираем самую старую.
Она не всегда будет самой старшей в абсолютном смысле.
Но чаще всего мы попадаем в одну n-ю часть, где n — количество выбранных нами выборок.
В настоящее время алгоритм случайного LRU не является полностью устойчивым к сканированию, но у нас уже есть реализация алгоритма случайного LRU 2, который используется в Apache Ignite для другой задачи.
А когда мы используем случайный LRU 2 для вытеснения страниц, проблема сопротивления полному сканированию будет решена.
Вытеснение страниц оказывает существенное влияние на задержку отдельной операции кэша.
Худшая ситуация: у вас в кеше был какой-то малоиспользуемый SQL-индекс или регион, и оказалось, что абсолютно все страницы этого региона были вытеснены на диск, то есть выброшены из памяти.
Если вы получите доступ к некоторому ключу, который будет иметь доступ ко всем n страницам, они будут последовательно прочитаны с диска.
И нам нужно минимизировать объем возможного чтения с диска.
Начиная с Apache Ignite 2.3 появилась возможность разделять кэши на разные области данных.
Если вы знаете, что у вас есть подмножество «горячих» данных и вы, вероятно, будете с ним работать, а также есть подмножество исторических данных, то имеет смысл разделить эти подмножества на разные области данных.
Также для определения соотношения диск/память всегда следует отслеживать не медиану или среднее время доступа для одной операции в кеше, а процентили, поскольку они являются наиболее полным способом представления информации.
В худшем случае в одном проценте случаев задержка будет значительно выше, поскольку страницы придется читать с диска.
Если вы просто посмотрите на среднее значение, вы никогда не заметите эту особенность.
Если для вас важны строгие SLA, то вам просто необходимо проанализировать процентили при определении пропорций.
И последнее, о чем стоит упомянуть: не запускайте множество узлов Apache Ignite с включенным сохранением на одном физическом носителе.
Поскольку физически диск один, количество операций ввода-вывода в секунду делится между узлами Apache Ignite. Вы не только разделяете пропускную способность между узлами, но и каждый узел может исчерпать емкость операций ввода-вывода в секунду, что приведет к хаотичному поведению всего кластера.
Если по какой-то причине вы хотите запустить несколько узлов Apache Ignite на одной машине, то обязательно убедитесь, что физическое хранилище для узлов разное.
Это в дополнение к рекомендации перенести журнал упреждающей записи на отдельный физический носитель.
Планирование пропускной способности ЦП и сети
Не следует использовать сеть с пропускной способностью менее 1 гигабит. Сегодня мало кто имеет сети с меньшей пропускной способностью.Выбор ЦП во многом зависит от профиля нагрузки и количества индексов.
Здесь стоит вернуться к эмпирическому подходу и просто сформировать профиль ожидаемой нагрузки для вашего приложения и внимательно следить за всеми показателями системы.
Если вы видите, что один из ресурсов полностью исчерпан, то имеет смысл его добавить.
Мы приветствуем любые вопросы или идеи по улучшению Apache Ignite. Присоединяйтесь к нашим встречам в Москве И Санкт-Петербург .
Другие интересные видео на нашем канале:
Теги: #программирование #Администрирование баз данных #java #Распределенные системы #apache ignite-
Coub Анонсировал Интеграцию Видео В Telegram
19 Oct, 24 -
Привязка Cloudflare Php Api
19 Oct, 24 -
Яндекс.каталог Крашится Или Взломан?
19 Oct, 24