Eine Anleitung zu Apache Ignite

1. Einleitung

Apache Ignite ist eine Open Source speicherzentrierte verteilte Plattform. Wir können es als Datenbank, als Caching-System oder für die In-Memory-Datenverarbeitung verwenden.

Die Plattform verwendet Speicher als Speicherschicht und weist daher eine beeindruckende Leistungsrate auf. Einfach ausgedrückt ist dies eine der schnellsten Plattformen für die Verarbeitung atomarer Daten, die derzeit in der Produktion eingesetzt werden.

2. Installation und Einrichtung

Auf der Seite Erste Schritte finden Sie zunächst die Anweisungen zur Ersteinrichtung und Installation.

Die Maven-Abhängigkeiten für die Anwendung, die wir erstellen werden:

 org.apache.ignite ignite-core ${ignite.version}   org.apache.ignite ignite-indexing ${ignite.version} 

ignite-core ist die einzige obligatorische Abhängigkeit für das Projekt . Da wir auch mit SQL interagieren möchten,ist auch die Ignite-Indizierung hier. $ {ignite.version} ist die neueste Version von Apache Ignite.

Als letzten Schritt starten wir den Ignite-Knoten:

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]

Die Konsolenausgabe oben zeigt, dass wir bereit sind.

3. Speicherarchitektur

Die Plattform basiert auf einer dauerhaften Speicherarchitektur . Dies ermöglicht das Speichern und Verarbeiten der Daten sowohl auf der Festplatte als auch im Speicher. Es erhöht die Leistung, indem die RAM-Ressourcen des Clusters effektiv genutzt werden.

Die Daten im Speicher und auf der Festplatte haben dieselbe binäre Darstellung. Dies bedeutet keine zusätzliche Konvertierung der Daten beim Übergang von einer Ebene zur anderen.

Die dauerhafte Speicherarchitektur wird in Blöcke fester Größe aufgeteilt, die als Seiten bezeichnet werden. Seiten werden außerhalb des Java-Heaps gespeichert und in einem RAM organisiert. Es hat eine eindeutige Kennung: FullPageId .

Seiten interagieren mit dem Speicher mithilfe der PageMemory- Abstraktion.

Es hilft, eine Seite zu lesen, zu schreiben und eine Seiten-ID zuzuweisen. Im Speicher ordnet Ignite Seiten Speicherpuffern zu .

4. Speicherseiten

Eine Seite kann folgende Zustände haben:

  • Entladen - kein Seitenpuffer im Speicher geladen
  • Löschen - Der Seitenpuffer wird geladen und mit den Daten auf der Festplatte synchronisiert
  • Dauer - Der Seitenpuffer enthält Daten, die sich von denen auf der Festplatte unterscheiden
  • Im Checkpoint verschmutzt - Es wird eine weitere Änderung gestartet, bevor die erste auf der Festplatte verbleibt. Hier startet ein Checkpoint und PageMemory speichert zwei Speicherpuffer für jede Seite.

Der dauerhafte Speicher weist lokal ein Speichersegment zu, das als Datenbereich bezeichnet wird . Standardmäßig hat es eine Kapazität von 20% des Cluster-Speichers. Durch die Konfiguration mehrerer Regionen können die verwendbaren Daten in einem Speicher gespeichert werden.

Die maximale Kapazität der Region ist ein Speichersegment. Es ist ein physischer Speicher oder ein kontinuierliches Byte-Array.

Um Speicherfragmentierungen zu vermeiden, enthält eine einzelne Seite mehrere Schlüsselwerteinträge . Jeder neue Eintrag wird der optimalsten Seite hinzugefügt. Wenn die Größe des Schlüssel-Wert-Paares die maximale Kapazität der Seite überschreitet, speichert Ignite die Daten auf mehr als einer Seite. Die gleiche Logik gilt für die Aktualisierung der Daten.

SQL- und Cache-Indizes werden in Strukturen gespeichert, die als B + Trees bezeichnet werden. Cache-Schlüssel werden nach ihren Schlüsselwerten sortiert.

5. Lebenszyklus

Jeder Ignite-Knoten wird auf einer einzelnen JVM-Instanz ausgeführt . Es ist jedoch möglich, mehrere Ignite-Knoten in einem einzigen JVM-Prozess auszuführen.

Lassen Sie uns die Lebenszyklusereignistypen durchgehen:

  • BEFORE_NODE_START - vor dem Start des Ignite-Knotens
  • AFTER_NODE_START - wird unmittelbar nach dem Start des Ignite-Knotens ausgelöst
  • BEFORE_NODE_STOP - vor dem Starten des Knotenstopps
  • AFTER_NODE_STOP - nachdem der Ignite-Knoten gestoppt wurde

So starten Sie einen Standard-Ignite-Knoten:

Ignite ignite = Ignition.start();

Oder aus einer Konfigurationsdatei:

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

Für den Fall, dass wir mehr Kontrolle über den Initialisierungsprozess benötigen, gibt es mithilfe der LifecycleBean- Schnittstelle einen anderen Weg :

public class CustomLifecycleBean implements LifecycleBean { @Override public void onLifecycleEvent(LifecycleEventType lifecycleEventType) throws IgniteException { if(lifecycleEventType == LifecycleEventType.AFTER_NODE_START) { // ... } } }

Hier können wir die Lebenszyklusereignistypen verwenden, um Aktionen vor oder nach dem Start / Stopp des Knotens auszuführen.

Zu diesem Zweck übergeben wir die Konfigurationsinstanz mit der CustomLifecycleBean an die Startmethode :

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

6. In-Memory-Datenraster

Ignite Data Grid ist ein verteilter Schlüsselwertspeicher , der der partitionierten HashMap sehr vertraut ist . Es ist horizontal skaliert. Dies bedeutet, dass mehr Clusterknoten hinzugefügt werden und mehr Daten zwischengespeichert oder im Speicher gespeichert werden.

It can provide significant performance improvement to the 3rd party software, like NoSql, RDMS databases as an additional layer for caching.

6.1. Caching Support

The data access API is based on JCache JSR 107 specification.

As an example, let's create a cache using a template configuration:

IgniteCache cache = ignite.getOrCreateCache( "baeldingCache");

Let's see what's happening here for more details. First, Ignite finds the memory region where the cache stored.

Then, the B+ tree index Page will be located based on the key hash code. If the index exists, a data Page of the corresponding key will be located.

When the index is NULL, the platform creates the new data entry by using the given key.

Next, let's add some Employee objects:

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

Again, the durable memory will look for the memory region where the cache belongs. Based on the cache key, the index page will be located in a B+ tree structure.

When the index page doesn't exist, a new one is requested and added to the tree.

Next, a data page is assigning to the index page.

To read the employee from the cache, we just use the key value:

Employee employee = cache.get(1);

6.2. Streaming Support

In memory data streaming provides an alternative approach for the disk and file system based data processing applications. The Streaming API splits the high load data flow into multiple stages and routes them for processing.

We can modify our example and stream the data from the file. First, we define a data streamer:

IgniteDataStreamer streamer = ignite .dataStreamer(cache.getName());

Next, we can register a stream transformer to mark the received employees as employed:

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

As a final step, we iterate over the employees.txt file lines and convert them into Java objects:

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)));

With the use of streamer.addData() put the employee objects into the stream.

7. SQL Support

The platform provides memory-centric, fault-tolerant SQL database.

We can connect either with pure SQL API or with JDBC. SQL syntax here is ANSI-99, so all the standard aggregation functions in the queries, DML, DDL language operations are supported.

7.1. JDBC

To get more practical, let's create a table of employees and add some data to it.

For that purpose, we register a JDBC driver and open a connection as a next step:

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

With the help of the standard DDL command, we populate the Employee table:

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

After the WITH keyword, we can set the cache configuration template. Here we use the REPLICATED. By default, the template mode is PARTITIONED. To specify the number of copies of the data we can also specify BACKUPS parameter here, which is 0 by default.

Then, let's add up some data by using INSERT DML statement:

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(); // add the rest 

Afterward, we select the records:

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

7.2. Query the Objects

It's also possible to perform a query over Java objects stored in the cache. Ignite treats Java object as a separate SQL record:

IgniteCache cache = ignite.cache("baeldungCache"); SqlFieldsQuery sql = new SqlFieldsQuery( "select name from Employee where isEmployed = 'true'"); QueryCursor
    
      cursor = cache.query(sql); for (List row : cursor) { // do something with the row }
    

8. Summary

In this tutorial, we had a quick look at Apache Ignite project. This guide highlights the advantages of the platform over other simial products such as performance gains, durability, lightweight APIs.

Als Ergebnis haben wir gelernt, wie Sie die SQL-Sprache und die Java-API zum Speichern, Abrufen und Streamen der Daten innerhalb des Persistenz- oder In-Memory-Rasters verwenden.

Wie üblich ist der vollständige Code für diesen Artikel auf GitHub verfügbar.