将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を動かして遊ぶ