1. 序章

Apache Igniteは、オープンソースのメモリ中心の分散プラットフォームです。 データベース、キャッシングシステム、またはメモリ内のデータ処理に使用できます。

プラットフォームはストレージレイヤーとしてメモリを使用するため、優れたパフォーマンスレートを実現します。 簡単に言えば、これは現在本番環境で使用されている最速のアトミックデータ処理プラットフォームの1つです。

2. インストールとセットアップ

まず、スタートページで初期設定とインストール手順を確認してください。

構築するアプリケーションの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}はApacheIgniteの最新バージョンです。

最後のステップとして、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]

上記のコンソール出力は、準備ができていることを示しています。

3. メモリアーキテクチャ

プラットフォームは耐久性のあるメモリアーキテクチャに基づいています。 これにより、データをディスクとメモリの両方に保存して処理できます。 クラスタのRAMリソースを有効に活用することでパフォーマンスを向上させます。

メモリ内とディスク上のデータは同じバイナリ表現を持っています。 これは、あるレイヤーから別のレイヤーに移動するときに、データの追加の変換がないことを意味します。

耐久性のあるメモリアーキテクチャは、ページと呼ばれる固定サイズのブロックに分割されます。 ページはJavaヒープの外部に保存され、RAMに編成されます。 一意の識別子があります:FullPageId

ページは、PageMemory抽象化を使用してメモリと対話します。

ページの読み取り、書き込み、およびページIDの割り当てに役立ちます。 メモリ内で、Igniteはページをメモリバッファに関連付けます。

4. メモリページ

ページは次の状態を持つことができます。

  • アンロード–メモリにページバッファがロードされていません
  • クリア–ページバッファがロードされ、ディスク上のデータと同期されます
  • 耐久性–ページバッファはディスク内のデータとは異なるデータを保持します
  • チェックポイントが汚れている–最初の変更がディスクに保持される前に別の変更が開始されます。 ここでチェックポイントが開始され、PageMemoryはページごとに2つのメモリバッファを保持します。

耐久性のあるメモリは、データ領域と呼ばれるメモリセグメントをローカルに割り当てます。デフォルトでは、クラスタメモリの容量は20% ofです。 複数リージョン構成により、使用可能なデータをメモリに保持できます。

領域の最大容量はメモリセグメントです。 これは、物理メモリまたは連続バイト配列です。

メモリの断片化を回避するために、1つのページに複数のキー値エントリが保持されます。 すべての新しいエントリは、最適なページに追加されます。 キーと値のペアのサイズがページの最大容量を超える場合、Igniteはデータを複数のページに保存します。 同じロジックがデータの更新にも適用されます。

SQLおよびキャッシュインデックスは、B+ツリーと呼ばれる構造に格納されます。 キャッシュキーは、キー値の順に並べられています。

5. ライフサイクル

各Igniteノードは単一のJVMインスタンスで実行されます。 ただし、単一のJVMプロセスで複数のIgniteノードを実行するように構成することは可能です。

ライフサイクルイベントの種類を見てみましょう。

  • BEFORE_NODE_START –Igniteノードの起動前
  • AFTER_NODE_START –Igniteノードの開始直後に発生します
  • BEFORE_NODE_STOP –ノード停止を開始する前
  • AFTER_NODE_STOP –Igniteノードが停止した後

デフォルトのIgniteノードを開始するには:

Ignite ignite = Ignition.start();

または構成ファイルから:

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

初期化プロセスをさらに制御する必要がある場合は、LifecycleBeanインターフェイスを使用する別の方法があります。

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

ここでは、ライフサイクルイベントタイプを使用して、ノードの開始/停止の前後にアクションを実行できます。

そのために、CustomLifecycleBeanを含む構成インスタンスをstartメソッドに渡します。

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

6. インメモリデータグリッド

Igniteデータグリッドは分散型Key-Valueストレージであり、パーティション化されたHashMapに非常に馴染みがあります。 水平方向に拡大縮小されます。 これは、追加するクラスターノードが増えることを意味し、より多くのデータがメモリにキャッシュまたは保存されます。

キャッシュの追加レイヤーとして、NoSqlやRDMSデータベースなどのサードパーティソフトウェアのパフォーマンスを大幅に向上させることができます。

6.1. キャッシングサポート

データアクセスAPIは、JCacheJSR107仕様に基づいています。

例として、テンプレート構成を使用してキャッシュを作成しましょう。

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

詳細については、ここで何が起こっているかを見てみましょう。 まず、Igniteは、キャッシュが保存されているメモリ領域を見つけます。

次に、キーハッシュコードに基づいてB+ツリーインデックスページが検索されます。 インデックスが存在する場合は、対応するキーのデータページが検索されます。

インデックスがNULLの場合、プラットフォームは指定されたキーを使用して新しいデータエントリを作成します。

次に、いくつかのEmployeeオブジェクトを追加しましょう。

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

この場合も、永続メモリはキャッシュが属するメモリ領域を探します。 キャッシュキーに基づいて、インデックスページはB+ツリー構造に配置されます。

インデックスページが存在しない場合は、新しいページが要求され、ツリーに追加されます。

次に、データページがインデックスページに割り当てられています。

キャッシュから従業員を読み取るには、次のキー値を使用します。

Employee employee = cache.get(1);

6.2. ストリーミングサポート

インメモリデータストリーミングは、ディスクおよびファイルシステムベースのデータ処理アプリケーションに代替アプローチを提供します。 ストリーミングAPIは、高負荷のデータフローを複数のステージに分割し、処理のためにルーティングします

例を変更して、ファイルからデータをストリーミングできます。 まず、データストリーマーを定義します。

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

次に、ストリームトランスを登録して、受け取った従業員を雇用済みとしてマークできます。

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

最後のステップとして、 employees.txt ファイルの行を繰り返し処理し、それらをJavaオブジェクトに変換します。

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()を使用して、従業員オブジェクトをストリームに配置します。

7. SQLサポート

このプラットフォームは、メモリ中心のフォールトトレラントSQLデータベースを提供します。

純粋なSQLAPIまたはJDBCのいずれかで接続できます。 ここでのSQL構文はANSI-99であるため、クエリ、DML、DDL言語操作のすべての標準集計関数がサポートされています。

7.1. JDBC

より実用的にするために、従業員のテーブルを作成し、それにいくつかのデータを追加しましょう。

そのために、次のステップとして JDBCドライバーを登録し、接続を開きます。

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

標準のDDLコマンドを使用して、Employeeテーブルにデータを入力します。

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

WITHキーワードの後に、キャッシュ構成テンプレートを設定できます。 ここでは、REPLICATEDを使用します。 デフォルトでは、テンプレートモードはPARTITIONEDです。 データのコピー数を指定するために、ここでBACKUPSパラメーターを指定することもできます。これはデフォルトでは0です。

次に、INSERTDMLステートメントを使用していくつかのデータを追加しましょう。

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

その後、レコードを選択します。

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

7.2. オブジェクトをクエリする

キャッシュに格納されているJavaオブジェクトに対してクエリを実行することもできます。 Igniteは、Javaオブジェクトを個別のSQLレコードとして扱います。

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. 概要

このチュートリアルでは、ApacheIgniteプロジェクトについて簡単に説明しました。 このガイドでは、パフォーマンスの向上、耐久性、軽量APIなど、他の類似製品に対するプラットフォームの利点を強調しています。

その結果、SQL言語とJavaAPIを使用して、永続グリッドまたはメモリ内グリッド内のデータを保存、取得、ストリーミングする方法を学びました。

いつものように、この記事の完全なコードは、GitHubから入手できます。