1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Apache Igniteを分散RDBとして使う

Last updated at Posted at 2019-05-19

はじめに

Apache Igniteを分散RDBとして使ってみます。
分散RDBとしてApache Igniteを見た時の特徴は以下のとおりです。

  • ANSI-99準拠
  • スケールアウト可能
  • 分散SQL JOINのサポート。各ノードのローカルデータに対してJOINが実行される。
  • ACID準拠のトランザクション。MVCCを可能としたトランザクション対応。ただし、v2.7(2018年12月リリース)ではベータ版。

環境

以下の環境を使用しました。

  • CentOS 7.5
  • Apache Ignite 2.7

Apache Igniteの環境は以下の手順で構築済みです。

テーブルを作成する

まず、SQLLineを使用してクラスタに接続し、コマンドラインからSQL文を実行しテーブルを作成します。
JDBCで接続してテーブルを作成することもできますが、今回はコマンドラインからにしました。
まずは、SQLLineを実行して、クラスタに接続します。

# /opt/apache-ignite/bin/sqlline.sh --verbose=true -u jdbc:ignite:thin://127.0.0.1/
issuing: !connect jdbc:ignite:thin://127.0.0.1/ '' '' org.apache.ignite.IgniteJdbcThinDriver
Connecting to jdbc:ignite:thin://127.0.0.1/
Connected to: Apache Ignite (version 2.7.0#20181130-sha1:256ae401)
Driver: Apache Ignite Thin JDBC Driver (version 2.7.0#20181130-sha1:256ae401)
Autocommit status: true
Transaction isolation: TRANSACTION_REPEATABLE_READ
sqlline version 1.3.0
0: jdbc:ignite:thin://127.0.0.1/> 

次にテーブルを作成します。
"ATOMICITY=TRANSACTIONAL_SNAPSHOT"と指定することで、SQLトランザクションに対応できます。なお、1つのSQLトランザクションで複数のテーブルを対象にする場合は、対象の全てのテーブルをTRANSACTIONAL_SNAPSHOTモードで作成する必要があります。

0: jdbc:ignite:thin://127.0.0.1/> CREATE TABLE City (
   id LONG PRIMARY KEY, name VARCHAR)
   WITH "template=replicated, ATOMICITY=TRANSACTIONAL_SNAPSHOT";
No rows affected (0.483 seconds)
0: jdbc:ignite:thin://127.0.0.1/> CREATE TABLE Person (
   id LONG, name VARCHAR, city_id LONG, PRIMARY KEY (id, city_id))
   WITH "backups=1, affinityKey=city_id, ATOMICITY=TRANSACTIONAL_SNAPSHOT";
No rows affected (0.27 seconds)

作成されたテーブルを"!tables"コマンドで確認します。

0: jdbc:ignite:thin://127.0.0.1/> !tables
+--------------------------------+--------------------------------+--------------------------------+--------------------------------+--------------------------------+--------------------+
|           TABLE_CAT            |          TABLE_SCHEM           |           TABLE_NAME           |           TABLE_TYPE           |            REMARKS             |            TYPE_CA |
+--------------------------------+--------------------------------+--------------------------------+--------------------------------+--------------------------------+--------------------+
|                                | PUBLIC                         | CITY                           | TABLE                          |                                |                    |
|                                | PUBLIC                         | PERSON                         | TABLE                          |                                |                    |
+--------------------------------+--------------------------------+--------------------------------+--------------------------------+--------------------------------+--------------------+
0: jdbc:ignite:thin://127.0.0.1/> 

次にインデックスを作成します。

0: jdbc:ignite:thin://127.0.0.1/> CREATE INDEX idx_city_name ON City (name);
No rows affected (0.079 seconds)
0: jdbc:ignite:thin://127.0.0.1/> 
0: jdbc:ignite:thin://127.0.0.1/> CREATE INDEX idx_person_name ON Person (name);
No rows affected (0.036 seconds)

データを投入します。

0: jdbc:ignite:thin://127.0.0.1/> INSERT INTO City (id, name) VALUES (1, 'Forest Hill');
1 row affected (0.173 seconds)
0: jdbc:ignite:thin://127.0.0.1/> INSERT INTO City (id, name) VALUES (2, 'Denver');
1 row affected (0.039 seconds)
0: jdbc:ignite:thin://127.0.0.1/> INSERT INTO City (id, name) VALUES (3, 'St. Petersburg');
1 row affected (0.042 seconds)
0: jdbc:ignite:thin://127.0.0.1/> 
0: jdbc:ignite:thin://127.0.0.1/> INSERT INTO Person (id, name, city_id) VALUES (1, 'John Doe', 3);
1 row affected (0.13 seconds)
0: jdbc:ignite:thin://127.0.0.1/> INSERT INTO Person (id, name, city_id) VALUES (2, 'Jane Roe', 2);
1 row affected (0.018 seconds)
0: jdbc:ignite:thin://127.0.0.1/> INSERT INTO Person (id, name, city_id) VALUES (3, 'Mary Major', 1);
1 row affected (0.014 seconds)
0: jdbc:ignite:thin://127.0.0.1/> INSERT INTO Person (id, name, city_id) VALUES (4, 'Richard Miles', 2);
1 row affected (0.008 seconds)

SELECT文を実行してみると、以下のように結果が取得できます。

0: jdbc:ignite:thin://127.0.0.1/> SELECT p.name, c.name
 FROM Person p, City c
 WHERE p.city_id = c.id;
+--------------------------------+--------------------------------+
|              NAME              |              NAME              |
+--------------------------------+--------------------------------+
| Jane Roe                       | Denver                         |
| Richard Miles                  | Denver                         |
| Mary Major                     | Forest Hill                    |
| John Doe                       | St. Petersburg                 |
+--------------------------------+--------------------------------+
4 rows selected (0.081 seconds)

Javaプログラムからクエリを実行する

JavaからIgniteに接続するためには、JDBC Client DriverかJDBC Driverを使用します。
違いはドキュメントに記載されていますが、どう使い分ければよいのかはよく分からなかったので、とりあえず今回はJDBC Driver(JDBC Thin Driver)を使用してみます。

以下、簡単なサンプルプログラムです。
(メイン関数しかない雑なプログラムですが)

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class SimpleSQLApp
{
	public static void main(String[] args) throws SQLException {

        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://192.168.20.71/")) {
        	conn.setAutoCommit(false);

        	System.out.println("select 1 -> ");
        	try (Statement stmt = conn.createStatement()) {
                try (ResultSet rs =
                    stmt.executeQuery("SELECT p.name, c.name FROM Person p INNER JOIN City c on c.id = p.city_id")) {
                    while (rs.next())
                        System.out.println(rs.getString(1) + ", " + rs.getString(2));
                }
            }

            try (PreparedStatement stmt = conn.prepareStatement("INSERT INTO city (id, name) VALUES (?, ?)")) {
                stmt.setLong(1, 99L);
                stmt.setString(2, "Test Location");
                stmt.executeUpdate();
            }

            try (PreparedStatement stmt =
                conn.prepareStatement("INSERT INTO person (id, name, city_id) values (?, ?, ?)")) {
                stmt.setLong(1, 99L);
                stmt.setString(2, "Test Person");
                stmt.setLong(3, 99L);
                stmt.executeUpdate();
            }
            conn.commit();
//          conn.rollback();

            System.out.println("select 2 -> ");
        	try (Statement stmt = conn.createStatement()) {
                try (ResultSet rs =
                    stmt.executeQuery("SELECT p.name, c.name FROM Person p INNER JOIN City c on c.id = p.city_id")) {
                    while (rs.next())
                        System.out.println(rs.getString(1) + ", " + rs.getString(2));
                }
            }

            try (PreparedStatement stmt = conn.prepareStatement("DELETE FROM city WHERE id = ?")) {
                stmt.setLong(1, 99L);
                stmt.executeUpdate();
            }

            try (PreparedStatement stmt =
                conn.prepareStatement("DELETE FROM person WHERE id = ?")) {
                stmt.setLong(1, 99L);
                stmt.executeUpdate();
            }
            conn.commit();
        }
    }
}

最後に

かなりあっさりと書きましたが、今回は少し使ってみたかっただけなのでこんなところで終了。

トランザクション処理は1台構成の場合は正常に処理できたのですが、2台構成(クラスタ)にすると、INSERT文の結果が返ってこない現象が発生しました。
色々試してみましたが、結局うまくいかなったので、次のバージョンが出たらまた試してみる予定。

[2019-05-20追記]
JDBC Client Driverでも試してみましたが、トランザクションはサポートしていないと怒られました。
サンプルプログラムは以下のようになります。
注意点は

  • テーブル名はスキーマ名をつけてあげないといけない(例:public.Person)
  • 設定ファイルが必要(今回はdefault-config.xmlを作成)
  • 設定ファイルが必要に関連して「ignite-spring」が必要になる
    です。

不明点はcache名を指定しないと"Ouch! Argument is invalid: Cache name must not be null or empty."というエラーが発生したこと。
試しに、cache=SQL_PUBLIC_CITYを指定したところうまく動くようになった。SQL_PUBLIC_PERSIONキャッシュにもアクセスしているが、こちらは指定しなかったのにエラーにならなかった。謎

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.apache.ignite.Ignition;

public class SimpleSQLAppJDBCClient
{
	public static void main(String[] args) throws SQLException, ClassNotFoundException {

		Ignition.setClientMode(true);
		Class.forName("org.apache.ignite.IgniteJdbcDriver");

        try (Connection conn =         try (Connection conn = DriverManager.getConnection("jdbc:ignite:cfg://cache=SQL_PUBLIC_CITY@file:///C:/pleiades/workspace/igniteclient-sql-indexing/src/main/java/default-config.xml")) {

        	System.out.println("select 1 -> ");
        	try (Statement stmt = conn.createStatement()) {
                try (ResultSet rs =
                    stmt.executeQuery("SELECT p.name, c.name FROM public.Person p INNER JOIN public.City c on c.id = p.city_id")) {
                    while (rs.next())
                        System.out.println(rs.getString(1) + ", " + rs.getString(2));
                }
            }

            try (PreparedStatement stmt = conn.prepareStatement("INSERT INTO city (id, name) VALUES (?, ?)")) {
                stmt.setLong(1, 99L);
                stmt.setString(2, "Test Location");
                stmt.executeUpdate();
            }

            try (PreparedStatement stmt =
                conn.prepareStatement("INSERT INTO person (id, name, city_id) values (?, ?, ?)")) {
                stmt.setLong(1, 99L);
                stmt.setString(2, "Test Person");
                stmt.setLong(3, 99L);
                stmt.executeUpdate();
            }

            System.out.println("select 2 -> ");
        	try (Statement stmt = conn.createStatement()) {
                try (ResultSet rs =
                    stmt.executeQuery("SELECT p.name, c.name FROM Person p INNER JOIN City c on c.id = p.city_id")) {
                    while (rs.next())
                        System.out.println(rs.getString(1) + ", " + rs.getString(2));
                }
            }

            try (PreparedStatement stmt = conn.prepareStatement("DELETE FROM city WHERE id = ?")) {
                stmt.setLong(1, 99L);
                stmt.executeUpdate();
            }

            try (PreparedStatement stmt =
                conn.prepareStatement("DELETE FROM person WHERE id = ?")) {
                stmt.setLong(1, 99L);
                stmt.executeUpdate();
            }
        }
    }
}

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?