将Apache Ignite用作为分布式关系数据库

首先

让我们试用Apache Ignite作为分布式关系数据库。
当将Apache Ignite视为分布式关系数据库时,以下是它的特点。

    • ANSI-99準拠

 

    • スケールアウト可能

 

    • 分散SQL JOINのサポート。各ノードのローカルデータに対してJOINが実行される。

 

    ACID準拠のトランザクション。MVCCを可能としたトランザクション対応。ただし、v2.7(2018年12月リリース)ではベータ版。

环境

我們使用了以下環境。

    • CentOS 7.5

 

    Apache Ignite 2.7

Apache Ignite的环境已按以下步骤构建完成。

    インメモリデータグリッド Apache IgniteをJavaのクライアントからアクセスする

创建一个桌子

首先,使用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事务具备可支持性。值得注意的是,如果要在一个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客户端驱动程序或JDBC驱动程序。虽然这些之间有所区别,但由于不太清楚该如何选择,所以我暂时选择使用JDBC驱动程序(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();
        }
    }
}

最终

我花了不少时间来写这篇文章,但这只是为了试试看,所以就到这里吧。

当使用单台结构进行事务处理时,可以正常进行处理。然而,当使用两台构成(集群)时,发生了无法获取INSERT语句结果的现象。
我们已经尝试了各种方法,但最终都没有成功,所以计划在下一个版本发布时再次尝试。

[2019-05-20追記]
我也尝试了使用JDBC客户端驱动程序,但是被告知它不支持事务。
示例程序如下:
注意事项包括:
– 表名必须加上模式名称(例如:public.Person)。
– 需要配置文件(本例中创建了default-config.xml)。
– 需要与配置文件相关的 “ignite-spring”。

当不指定缓存名称时,会发生错误,错误信息为”噢!参数无效:缓存名称不能为空。”。尝试指定缓存名称为 “SQL_PUBLIC_CITY” 后,程序成功运行。虽然也访问了 “SQL_PUBLIC_PERSON” 缓存,但没有指定名称却没有发生错误,这很奇怪。

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

请中国的母语者将下面的内容用中文进行释义,只需要提供一个选项:

参考

    • Apache Ignite公式 SQL

 

    • SQL Transactions(公式サイト)

 

    Apache Ignite上で、SQLを動かして遊ぶ
bannerAds