Connect SE JDBC 表类型:访问其他 DBMS 的表

在本篇博客中,我们将展示如何从 MariaDB Server 访问 MySQL 和 MariaDB DBMS 的表。为此,我们将使用 Connect Storage Engine (SE),它支持不同的表类型选项。在本例中,我们将使用 JDBC 表类型。要使用 JDBC 表类型,我们需要在 Connect SE 的 CREATE TABLE
定义中指定它,并且需要访问的 DBMS 的 Java 连接器。为了演示目的,我们将使用容器,但这同样适用于服务器运行在虚拟机或裸机上的情况。只需确保机器可以通过 TCP/IP 互相访问即可。只要有相应的 Java 连接器,同样的过程可用于访问任何其他 DBMS。
为了从 MariaDB Server 访问另一个 DBMS 的表,我们需要:
- 源 - 位于外部 DBMS 中的表,我们将从中“获取”数据。(在本例中,我们将使用 MariaDB 和 MySQL 作为“外部”DBMS。)
- 目标 - 位于 MariaDB Server 中的本地表。
- 能够与外部 DBMS 通信的 Java 连接器以及 JDBC 的依赖项。
- 在目标系统上安装了 Connect SE 插件。
- 访问外部 DBMS 和我们的源表的凭据。
如果您不想查看完整的设置过程,可以跳过下面的分步指南,通过运行第 6 节中的 docker-compose
脚本来查看连接的实际效果。您可以在此仓库中找到本博客文章中的文件。
1. 在 MySQL 和 MariaDB DBMS 中创建源表
我们将创建两个容器,mariadb-source
和 mysql-source
,两者都运行各自的数据库系统。我们将在两个容器中都创建一个表,并填充一些行。创建表的 SQL 命令将放在容器的特殊文件夹 /docker-entrypoint-initdb.d
内的 .sql
文件中。该目录下任何 SQL 文件都会在初始化新实例时运行。我们将使用卷挂载点来实现这一点。
1.1 MariaDB Server 源表
这是我们的 data.sql
文件内容:
$ cat mariadb_data/data.sql
CREATE TABLE t_maria (t int);
INSERT INTO t_maria VALUES (1),(2),(3);
为了让我们的容器互相通信,我们将创建一个自定义的 docker network
(用户定义的桥接网络)。这将使我们能够在容器之间实现自动 DNS 解析。默认的桥接网络不支持这一点。(更多详情见第 5 节)。
$ docker network create jdbc_connect
接下来,我们将启动 mariadb-source
容器。
$ docker container run \
--name mariadb-source \
--rm \
-e MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=1 \
-e MARIADB_DATABASE=db_maria \
-v$PWD/mariadb_data:/docker-entrypoint-initdb.d:z \
-d --network jdbc_connect \
mariadb:latest
容器初始化完成后,我们可以检查数据。
$ docker exec -t mariadb-source mariadb -e "select * from t_maria" db_maria
+------+
| t |
+------+
| 1 |
| 2 |
| 3 |
+------+
1.2 MySQL Server 源表
我们将对 MySQL 容器遵循类似的过程。这是我们的数据文件,放在不同的目录以便于挂载。
$ cat mysql_data/data.sql
CREATE TABLE t_mysql (t int);
INSERT INTO t_mysql VALUES (4),(5),(6);
让我们启动容器。
$ docker container run \
--name mysql-source \
--rm \
-e MYSQL_ALLOW_EMPTY_PASSWORD=1 \
-e MYSQL_DATABASE=db_mysql \
-v$PWD/mysql_data:/docker-entrypoint-initdb.d:z \
-d --network jdbc_connect \
mysql:latest
并检查数据。
$ docker exec -t mysql-source mysql -e "select * from t_mysql" db_mysql
+------+
| t |
+------+
| 4 |
| 5 |
| 6 |
+------+
1.3 MariaDB DBMS 目标
现在我们的源已经准备就绪,让我们创建目标 MariaDB Docker 容器。这个容器我们不会添加任何预加载数据。
$ docker container run --name mariadb-target --rm -e MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=1 -d --network jdbc_connect mariadb:latest
现在我们需要配置目标容器所需的依赖项。我们可以在 Dockerfile 中完成此操作,但让我们在容器中进行实时实验。
2. 源 DBMS 的 Java 连接器和依赖项
2.1 JDBC 的依赖项
让我们进入容器的 shell 并开始配置。
$ docker exec -it mariadb-target bash
JDBC 有以下要求: java
, jvm
和 jar
。前两个二进制文件是 openjdk-11-jre-headless
的一部分,但由于我们将在下方使用 jar
来验证某些 jar 文件的内容,我们将使用 openjdk-11-jdk-headless
包,它将 openjdk-11-jre-headless
作为依赖项。
$ apt update && apt install openjdk-11-jdk-headless
检查安装(java
, javac
, jar
),它们应该已经安装。
$ java --version
openjdk 11.0.18 2023-01-17
OpenJDK Runtime Environment (build 11.0.18+10-post-Ubuntu-0ubuntu122.04)
OpenJDK 64-Bit Server VM (build 11.0.18+10-post-Ubuntu-0ubuntu122.04, mixed mode, sharing)
此外,查找 libjvm.so
的位置。它应该位于 /usr/lib/jvm/java-11-openjdk-amd64/lib/server/libjvm.so
。我们需要将其指定为 connect_jvm_path 的一部分。
2.2 安装 Java 连接器
为了通过 JDBC 访问表,我们需要源 DBMS 的 Java 连接器。在本例中,我们需要在目标系统上安装 MySQL Connector/J 和 MariaDB Connector/J。我们还需要包装器文件。这些包装器文件是 mariadb-test-data
包的一部分。JavaWrappers.jar
包含 JdbcInterface 的 MySQL 和 MariaDB 接口的字节码子类。(给读者的说明:我们知道当前的打包方案并不理想,我们正在努力改进这一点,并将 JavaWrappers 放入适当的包中)。
$ apt install mariadb-test-data # Install the package
$ dpkg -L mariadb-test-data | \
grep JavaWrapper # Let's see where JavaWrappers was placed.
/usr/share/mysql/mysql-test/plugin/connect/connect/std_data/JavaWrappers.jar
jar 文件中有多个包装器。可以查看 jar 文件内容如下:
$ jar tf /usr/share/mysql/mysql-test/plugin/connect/connect/std_data/JavaWrappers.jar \
| grep class
wrappers/PostgresqlInterface.class
wrappers/OracleInterface.class
wrappers/ApacheInterface.class
wrappers/Client.class
wrappers/JdbcInterface.class
wrappers/MysqlInterface.class
wrappers/MariadbInterface.class
务必记住 JavaWrappers.jar
文件本身的路径,即 /usr/share/mysql/mysql-test/plugin/connect/connect/std_data/JavaWrappers.jar
,稍后将用作 connect_class_path。您可以自由移动此文件,但请务必记住其路径。
根据您要连接的数据库,您需要一个额外的 Java 连接器客户端。
对于 MariaDB Connector/J,以及 Debian/Ubuntu 中的 MySQL,我们可以使用 libmariadb-java
包。对于这个包,客户端位于 /usr/share/java/mariadb-java-client.jar
。我们需要将此路径附加到 connect_class_path
系统变量中,就像您为常规的 bash "PATH" 变量所做的那样。不同的路径通过 :
分隔。
3. 在目标系统上安装 Connect SE 插件
这一步非常简单,之前在上一篇博客中已经介绍过。由于我们已经登录到目标容器,让我们为其安装包。
$ apt install mariadb-plugin-connect
$ ls /usr/lib/mysql/plugin/ha_connect*
/usr/lib/mysql/plugin/ha_connect.so
这将在容器内安装插件,但 MariaDB Server 尚未知道它应该在启动时实际加载它。
4. 为 JDBC 配置目标 MariaDB Server
让我们创建一个配置文件,以便在 MariaDB Server 启动时加载 Connect SE,并设置查找 jvm
和 JavaWrappers 所需的配置变量。以下是必要的更改:
[mariadb]
plugin_load_add=ha_connect.so
connect_jvm_path=/usr/lib/jvm/java-11-openjdk-amd64/lib/server/
connect_class_path=/usr/share/mysql/mysql-test/plugin/connect/connect/std_data/JavaWrappers.jar:/usr/share/java/mariadb-java-client.jar
您可以将此配置块添加到 /etc/mysql/mariadb.conf.d/50-server.cnf
中,但您需要重新启动 docker 容器以使更改生效。
$ docker restart mariadb-target
另一种方法是在宿主机上创建一个自定义配置文件,并将其挂载到容器内的 /etc/mysql/mariadb.conf.d/
文件夹中。在第 6 节中使用 docker-compose
时,我们就是这样做的。
最后,让我们开始检查配置。首先检查 Connect SE 是否正确安装。它应该显示在 show plugins
中。
$ docker exec -it mariadb-target mariadb -uroot -e "show plugins soname like '%connect%';"
+---------+--------+----------------+---------------+---------+
| Name | Status | Type | Library | License |
+---------+--------+----------------+---------------+---------+
| CONNECT | ACTIVE | STORAGE ENGINE | ha_connect.so | GPL |
+---------+--------+----------------+---------------+---------+
其次,让我们检查 connect_jvm_path
和 connect_class_path
变量是否正确。
$ docker exec -t mariadb-target mariadb -e "select @@connect_jvm_path, @@connect_class_path\G"
*************************** 1. row ***************************
@@connect_jvm_path: /usr/lib/jvm/java-11-openjdk-amd64/lib/server/
@@connect_class_path: /usr/share/mysql/mysql-test/plugin/connect/connect/std_data/JavaWrappers.jar:/usr/share/java/mariadb-java-client.jar
5. 访问“外部源”表
我们将首先尝试访问位于“源”MariaDB Server 中的表。为此,我们将在本地 MariaDB Server 中创建一个表。该表将使用 Connect SE,并带有一些特殊的 JDBC 特定参数。这些参数指定表类型和连接 URL。表类型指示 Connect SE 使用什么驱动程序来获取数据,连接字符串则传递给驱动程序。
5.1 从 localhost 上的表获取数据
在我们尝试连接到源容器之前,让我们进行一次“回环”连接,以测试 JDBC 在目标容器中是否正常工作。首先启动一个 mariadb
客户端连接。
$ docker exec -it mariadb-target mariadb -u root
现在创建一个 test
数据库和一个示例表 t
。
MariaDB [(none)]> create database test; use test;
MariaDB [test]> create table t(t int); insert into t values (1),(2);
接下来创建一个 Connect SE 表,使用 JDBC 连接到它。
MariaDB [test]> create table t_jdbc engine=connect table_type=JDBC tabname=t connection='jdbc:mariadb://localhost/test?user=root&password';
MariaDB [test]> select * from t_jdbc;
+------+
| t |
+------+
| 1 |
| 2 |
+------+
5.2 从远程主机上的表获取数据 (mariadb-source 和 mysql-source)
由于我们在同一个网络上创建了所有容器,容器名称可以解析为容器的私有 IP 地址。
仍在 mariadb-target
容器中,创建一个表,其连接字符串指向 mariadb-source
。请注意,我们使用了数据库的 root
用户。由于我们在创建 mariadb-source
容器时创建了空密码的 root 用户,因此我们在 jdbc
连接字符串中没有传递密码。
MariaDB [test]> create table db_mariadb_target engine=connect table_type=JDBC tabname=t_maria connection='jdbc:mariadb://mariadb-source/db_maria?user=root&password'\G
MariaDB [test]> select * from db_mariadb_target;
+------+
| t |
+------+
| 1 |
| 2 |
| 3 |
+------+
类似地,要连接到我们的 mysql-source
容器,我们只需要调整连接字符串。
MariaDB [test]> create table db_mysql_target engine=connect table_type=JDBC tabname=t_mysql connection='jdbc:mariadb://mysql-source/db_mysql?user=root&password'\G
MariaDB [test]> select * from db_mysql_target;
+------+
| t |
+------+
| 4 |
| 5 |
| 6 |
+------+
太棒了!我们通过 JDBC 表类型从远程 DBMS 访问到了我们的目标(本地)容器。
5.3 修改 Connect SE 表
出于所有目的,应用程序可以像使用“目标”DBMS 的一部分一样使用此表。然而,如果我们想获得 InnoDB 的所有性能优势怎么办?好吧,我们可以将 Connect SE 表迁移到 InnoDB。数据不会随外部表的更改而更新,但我们将拥有转换为 InnoDB 时刻的所有数据。
MariaDB [test]> show tables;
+-------------------+
| Tables_in_test |
+-------------------+
| db_mariadb_target |
| db_mysql_target |
| t |
| t_jdbc |
+-------------------+
4 rows in set (0.000 sec)
为了导入数据,我们首先创建一个克隆表。然后对其运行 ALTER TABLE 以将存储引擎更改为 InnoDB。这样我们将保留 Connect SE 表属性,以防将来想要切换回来。
MariaDB [test]> create table temp like db_mysql_target;
MariaDB [test]> show create table temp\G
*************************** 1. row ***************************
Table: temp
Create Table: CREATE TABLE `temp` (
`t` int(10) DEFAULT NULL
) ENGINE=CONNECT DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci CONNECTION='jdbc:mysql://mysql-source/db_mysql?user=root&password' `TABLE_TYPE`='JDBC' `TABNAME`='t_mysql'
1 row in set (0.000 sec)
MariaDB [test]> alter table temp engine=InnoDB;
Query OK, 3 rows affected (0.037 sec)
Records: 3 Duplicates: 0 Warnings: 0
MariaDB [test]> show create table temp\G
*************************** 1. row ***************************
Table: temp
Create Table: CREATE TABLE `temp` (
`t` int(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci CONNECTION='jdbc:mysql://mysql-source/db_mysql?user=root&password' /* `TABLE_TYPE`='JDBC' `TABNAME`='t_mysql' */
1 row in set (0.000 sec)
6. 使用 Docker Compose 实现自动化
使用 Docker 和容器的好处在于,我们可以将大部分工作自动化。在此 GitHub 仓库中,您将找到一个 docker-compose 文件及相关脚本,只需运行 docker-compose up
即可实现相同的结果。
启动后,您可以直接检查表。
docker exec -t mariadb-target mariadb test -e "select 'mysql' as src,t from db_mysql_target union all select 'mariadb',t from db_mariadb_target"
+---------+------+
| src | t |
+---------+------+
| mysql | 4 |
| mysql | 5 |
| mysql | 6 |
| mariadb | 1 |
| mariadb | 2 |
| mariadb | 3 |
+---------+------+
阅读更多
欢迎反馈
如果您在本博客中遇到任何问题、设计上的问题或未按预期工作的边界情况,请告知我们。欢迎在 Zulip 上讨论。一如既往,您可以在我们的 JIRA bug/feature request 系统(MDEV 项目)中提交遇到的任何 bug/feature request。