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].