Руководство по Apache Ignite

1. Вступление

Apache Ignite - это распределенная платформа с открытым исходным кодом, ориентированная на память. Мы можем использовать его как базу данных, систему кэширования или для обработки данных в памяти.

Платформа использует память в качестве слоя хранения, поэтому имеет впечатляющую производительность. Проще говоря, это одна из самых быстрых платформ атомарной обработки данных, которые в настоящее время используются в производстве.

** 2. Установка и настройка

**

Для начала ознакомьтесь с getting начальная страница для начальной установки и инструкции по установке.

Зависимости Maven для приложения, которое мы собираемся построить:

<dependency>
    <groupId>org.apache.ignite</groupId>
    <artifactId>ignite-core</artifactId>
    <version>${ignite.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.ignite</groupId>
    <artifactId>ignite-indexing</artifactId>
    <version>${ignite.version}</version>
</dependency>
  • ignite-core является единственная обязательная зависимость для проекта ** . Поскольку мы также хотим взаимодействовать с SQL, ignite-indexing также здесь.

$\{ignite.version } - это последняя версия Apache Ignite.

В качестве последнего шага мы запускаем узел Ignite:

Ignite node started OK (id=53c77dea)
Topology snapshot[ver=1, servers=1, clients=0, CPUs=4, offheap=1.2GB, heap=1.0GB]Data Regions Configured:
^-- default[initSize=256.0 MiB, maxSize=1.2 GiB, persistenceEnabled=false]----

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

[[architecture]]

===  3. Архитектура памяти

**  Платформа основана на архитектуре Durable Memory ** . Это позволяет хранить и обрабатывать данные как на диске, так и в памяти. Это повышает производительность благодаря эффективному использованию ресурсов ОЗУ кластера.

Данные в памяти и на диске имеют одинаковое двоичное представление.

Это означает отсутствие дополнительного преобразования данных при перемещении с одного слоя на другой.

Надежная архитектура памяти разделяется на блоки фиксированного размера, называемые страницами.

Страницы хранятся вне кучи Java и организованы в ОЗУ. У него есть уникальный идентификатор: __FullPageId__.

Страницы взаимодействуют с памятью с помощью абстракции __PageMemory__.

Это помогает читать, написать страницу, а также выделить идентификатор страницы. **  Внутри памяти Ignite связывает страницы с __Memory Buffers__ ** .

[[pages]]

===  **  4. Страницы памяти **

Страница может иметь следующие состояния:

**  Unloaded - в памяти не загружен буфер страницы

**  Очистить - буфер страницы загружается и синхронизируется с данными на

диск
**  Durty - буфер страницы содержит данные, которые отличаются от данных

на диске
**  Грязный в контрольно-пропускном пункте - перед началом

Первый сохраняется на диске. Здесь начинается контрольная точка, и __PageMemory__ сохраняет два буфера памяти для каждой страницы.

**  Долговечная память выделяет локальный сегмент памяти с именем __Data Region __. **  По умолчанию он имеет емкость 20% памяти кластера. Конфигурация нескольких областей позволяет хранить используемые данные в памяти.

Максимальная емкость региона - сегмент памяти. Это физическая память или непрерывный байтовый массив.

**  Чтобы избежать фрагментации памяти, на одной странице хранится несколько записей значения ключа ** . Каждая новая запись будет добавлена ​​на наиболее оптимальную страницу. Если размер пары ключ-значение превышает максимальную емкость страницы, Ignite сохраняет данные на нескольких страницах. Та же логика применима к обновлению данных.

Индексы SQL и кеша хранятся в структурах, известных как деревья B +. Ключи кэша упорядочены по значениям ключей.

===  **  5. Жизненный цикл**

**  Каждый узел Ignite работает на одном экземпляре JVM ** . Тем не менее, можно настроить несколько узлов Ignite, работающих в одном процессе JVM.

Давайте рассмотрим типы событий жизненного цикла:

**  __BEFORE__NODE__START__ - до запуска узла Ignite

**  __AFTER__NODE__START__ - срабатывает сразу после запуска узла Ignite

**  __BEFORE__NODE__STOP__ - перед началом остановки узла

**  __AFTER__NODE__STOP__ - после остановки узла Ignite

Чтобы запустить узел Ignite по умолчанию:

[source,java,gutter:,true]

Ignite ignite = Ignition.start();

Или из файла конфигурации:

[source,java,gutter:,true]

Ignite ignite = Ignition.start("config/example-cache.xml");

В случае, если нам нужно больше контроля над процессом инициализации, есть другой способ с помощью интерфейса __LifecycleBean__:

[source,java,gutter:,true]

public class CustomLifecycleBean implements LifecycleBean {

@Override
public void onLifecycleEvent(LifecycleEventType lifecycleEventType)
  throws IgniteException {
        if(lifecycleEventType == LifecycleEventType.AFTER__NODE__START) {
           //...
        }
    }
}
Здесь мы можем использовать типы событий жизненного цикла для выполнения действий до или после запуска/остановки узла.

Для этого мы передаем экземпляр конфигурации с __CustomLifecycleBean__ методу запуска:

[source,java,gutter:,true]

IgniteConfiguration configuration = new IgniteConfiguration(); configuration.setLifecycleBeans(new CustomLifecycleBean()); Ignite ignite = Ignition.start(configuration);

[[data__grid]]

===  6. Сетка данных в памяти

**  Ignite Data Grid - это распределенное хранилище значений ключей ** , очень знакомое для секционированных __HashMap__. Это горизонтально масштабируется. Это означает, что мы добавляем больше узлов кластера, больше данных кэшируется или сохраняется в памяти.

Это может обеспечить значительное улучшение производительности стороннего программного обеспечения, такого как NoSql, базы данных RDMS в качестве дополнительного уровня для кэширования.

[[caching__support]]

====  **  6.1. Поддержка кеширования **

**  API доступа к данным основан на спецификации JCache JSR 107. **

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

[source,java,gutter:,true]

IgniteCache<Employee, Integer> cache = ignite.getOrCreateCache( "baeldingCache");

Давайте посмотрим, что здесь происходит, для более подробной информации. Сначала Ignite находит область памяти, где хранится кэш.

Затем страница индекса дерева B будет расположена на основе хэш-кода ключа.

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

**  Когда индекс равен NULL, платформа создает новую запись данных, используя заданный ключ. **

Теперь давайте добавим несколько __Employee__ объектов:

[source,java,gutter:,true]

cache.put(1, new Employee(1, "John", true)); cache.put(2, new Employee(2, "Anna", false)); cache.put(3, new Employee(3, "George", true));

Опять же, долговременная память будет искать область памяти, к которой относится кэш. На основе ключа кэша страница индекса будет расположена в виде древовидной структуры.

**  Когда индексная страница не существует, запрашивается новая страница и добавляется в дерево. **

Затем страница данных присваивается странице индекса.

Чтобы прочитать сотрудника из кэша, мы просто используем значение ключа:

[source,java,gutter:,true]

Employee employee = cache.get(1);

[[streaming__support]]

====  **  6.2. Поддержка потоковой передачи **

Потоковая передача данных в памяти обеспечивает альтернативный подход для приложений обработки данных на основе дисков и файловых систем. **  Streaming API разделяет поток данных с высокой нагрузкой на несколько этапов и направляет их для обработки ** .

Мы можем изменить наш пример и передавать данные из файла. Сначала мы определяем поток данных:

[source,java,gutter:,true]

IgniteDataStreamer<Integer, Employee> streamer = ignite .dataStreamer(cache.getName());

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

[source,java,gutter:,true]

streamer.receiver(StreamTransformer.frome, arg) → { Employee employee = e.getValue(); employee.setEmployed(true); e.setValue(employee); return employee; };

В качестве последнего шага мы перебираем строки файла __employees.txt__ и преобразуем их в объекты Java:

[source,java,gutter:,true]

Path path = Paths.get(IgniteStream.class.getResource("employees.txt") .toURI()); Gson gson = new Gson(); Files.lines(path) .forEach(l → streamer.addData( employee.getId(), gson.fromJson(l, Employee.class)));

**  С помощью __streamer.addData () __ поместите объекты сотрудников в поток. **

[[sql__support]]

===  **  7. Поддержка SQL **

**  Платформа предоставляет ориентированную на память, отказоустойчивую базу данных SQL. **

Мы можем соединиться либо с чистым SQL API, либо с JDBC. Синтаксис SQL здесь - ANSI-99, поэтому поддерживаются все стандартные функции агрегирования в запросах, операции языка DML, DDL.

====  **  7.1. JDBC **

Чтобы стать более практичным, давайте создадим таблицу сотрудников и добавим к ней некоторые данные.

Для этого **  мы регистрируем драйвер JDBC и открываем соединение **  как следующий шаг:

[source,java,gutter:,true]

Class.forName("org.apache.ignite.IgniteJdbcThinDriver"); Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/");

С помощью стандартной команды DDL мы заполняем таблицу __Employee__:

[source,java,gutter:,true]

sql.executeUpdate("CREATE TABLE Employee ("
" id LONG PRIMARY KEY, name VARCHAR, isEmployed tinyint(1)) "
" WITH \"template=replicated\"");

После ключевого слова WITH мы можем установить шаблон конфигурации кэша.

Здесь мы используем __REPLICATED__. **  По умолчанию режим шаблонов - __PARTITIONED__ ** . **  Чтобы указать количество копий данных, мы также можем указать здесь параметр __BACKUPS__ ** , который по умолчанию равен 0.

Затем добавим некоторые данные, используя оператор INSERT DML:

[source,java,gutter:,true]

PreparedStatement sql = conn.prepareStatement( "INSERT INTO Employee (id, name, isEmployed) VALUES (?, ?, ?)");

sql.setLong(1, 1); sql.setString(2, "James"); sql.setBoolean(3, true); sql.executeUpdate();

После этого мы выбираем записи:

[source,java,gutter:,true]

ResultSet rs = sql.executeQuery("SELECT e.name, e.isEmployed " + " FROM Employee e " + " WHERE e.isEmployed = TRUE ")

====  **  7.2. Запрос объектов **

**  Также возможно выполнить запрос к объектам Java, хранящимся в кэше ** . Ignite обрабатывает объект Java как отдельную запись SQL:

[source,java,gutter:,true]

IgniteCache<Integer, Employee> cache = ignite.cache("baeldungCache");

SqlFieldsQuery sql = new SqlFieldsQuery( "select name from Employee where isEmployed = 'true'");

QueryCursor<List<?>> cursor = cache.query(sql);

for (List<?> row : cursor) { //do something with the row }

===  **  8. Резюме**

В этом уроке мы кратко рассмотрели проект Apache Ignite. В этом руководстве подчеркиваются преимущества платформы по сравнению с другими аналогичными продуктами, такие как повышение производительности, долговечность, облегченные API.

В результате **  мы узнали, как использовать язык SQL и Java API для хранения, извлечения, потоковой передачи данных в сетке постоянства или в памяти. **

Как обычно, полный код этой статьи доступен на https://github.com/eugenp/tutorials/tree/master/libraries-data/[over на GitHub].