跳到主要内容

JDBC Catalog

JDBC Catalog 支持通过标准 JDBC 接口连接支持 JDBC 协议的数据库。

本文档介绍 JDBC Catalog 的通用配置和使用方法。不同的 JDBC 源请参阅对应的文档。

适用场景

JDBC Catalog 仅适用于数据集成,如将少量数据从数据源导入到 Doris 中,或对 JDBC 数据源中的小表进行关联查询。JDBC Catalog 无法对数据源进行查询加速,或一次性访问大量数据。

支持的数据库

Doris JDBC Catalog 支持连接以下数据库:

支持的数据源
MySQL
PostgreSQL
Oracle
SQL Server
IBM DB2
ClickHouse
SAP HANA
Oceanbase

配置 Catalog

语法

CREATE CATALOG [IF NOT EXISTS] catalog_name PROPERTIES (
'type' =='jdbc', -- required
{JdbcProperties},
{CommonProperties}
);
  • {JdbcProperties}

    • 必须属性

      | 参数名称          | 说明                                | 示例                     |
      | ------------- | --------------------------------- | ---------------------- |
      | `user` | 数据源用户名 | |
      | `password` | 数据源密码 | |
      | `jdbc_url` | 数据源连接 URL | `jdbc:mysql://host:3306` |
      | `driver_url` | 数据源 JDBC 驱动程序文件的路径。关于驱动包安全性,详见附录。 | 驱动程序支持三种方式,详见下面说明。 |
      | `driver_class` | 数据源 JDBC 驱动程序的类名 | |

      `driver_url` 支持以下三种指定方式:

      1. 文件名。如 `mysql-connector-j-8.3.0.jar`。需将 Jar 包预先存放在 FE 和 BE 部署目录下的 `jdbc_drivers/` 目录下。系统会自动在这个目录下寻找。该目录的位置,也可以由 `fe.conf` 和 `be.conf` 中的 `jdbc_drivers_dir` 配置修改。

      2. 本地绝对路径。如 `file:///path/to/mysql-connector-j-8.3.0.jar`。需将 Jar 包预先存放在所有 FE/BE 节点指定的路径下。

      3. Http 地址。如:`http://repo1.maven.org/maven2/com/mysql/mysql-connector-j/8.3.0/mysql-connector-j-8.3.0.jar` 系统会从这个 Http 地址下载 Driver 文件。仅支持无认证的 Http 服务。
    • 可选属性

      | 参数名称                              | 默认值     | 说明                                                                                                                |
      | --------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------- |
      | `lower_case_meta_names` | false | 是否以小写的形式同步外部数据源的库名和表名以及列名 |
      | `meta_names_mapping` | | 当外部数据源存在名称相同只有大小写不同的情况,例如 `MY_TABLE` 和 `my_table`,Doris 由于歧义而在查询 Catalog 时报错,此时需要配置 `meta_names_mapping` 参数来解决冲突。 |
      | `only_specified_database` | false | 是否只同步 `jdbc_url` 中指定的数据源的 Database(此处的 Database 为映射到 Doris 的 Database 层级) |
      | `connection_pool_min_size` | 1 | 定义连接池的最小连接数,用于初始化连接池并保证在启用保活机制时至少有该数量的连接处于活跃状态。 |
      | `connection_pool_max_size` | 30 | 定义连接池的最大连接数,每个 Catalog 对应的每个 FE 或 BE 节点最多可持有此数量的连接。 |
      | `connection_pool_max_wait_time` | 5000 | 如果连接池中没有可用连接,定义客户端等待连接的最大毫秒数。 |
      | `connection_pool_max_life_time` | 1800000 | 设置连接在连接池中保持活跃的最大时长(毫秒)。超时的连接将被回收。同时,此值的一半将作为连接池的最小逐出空闲时间,达到该时间的连接将成为逐出候选对象。 |
      | `connection_pool_keep_alive` | false | 仅在 BE 节点上有效,用于决定是否保持达到最小逐出空闲时间但未到最大生命周期的连接活跃。默认关闭,以减少不必要的资源使用。 |
  • {CommonProperties}

    CommonProperties 部分用于填写通用属性。请参阅 数据目录概述 中【通用属性】部分。

查询操作

基础查询

-- 1. switch to catalog, use database and query
SWITCH mysql_ctl;
USE mysql_db;
SELECT * FROM mysql_tbl LIMIT 10;

-- 2. use mysql database directly
USE mysql_ctl.mysql_db;
SELECT * FROM mysql_tbl LIMIT 10;

-- 3. use full qualified name to query
SELECT * FROM mysql_ctl.mysql_db.mysql_tbl LIMIT 10;

查询优化

谓词下推

JDBC Catalog 访问数据源,本质上是选择一台 BE 节点,通过 JDBC Client 将生成好的 SQL 发送给源端并获取数据,因此性能仅取决于生成的 SQL 在源端的执行效率。Doris 会尽量将谓词条件下推并拼接到生成的 SQL 中。可以通过 EXPLAIN SQL查看到生成的 SQL 语句。

EXPLAIN SELECT smallint_u, sum(int_u)
FROM all_types WHERE smallint_u > 10 GROUP BY smallint_u;

...
| 0:VJdbcScanNode(206) |
| TABLE: `doris_test`.`all_types` |
| QUERY: SELECT `smallint_u`, `int_u` FROM `doris_test`.`all_types` WHERE ((`smallint_u` > 10)) |
| PREDICATES: (smallint_u[#1] > 10) |
| final projections: smallint_u[#1], int_u[#3] |
| final project output tuple id: 1
... |

函数下推

默认不会下推带函数的谓词条件。因为对一同一个函数,可能在 Doris 和外部数据源中的语义或行为是不一致的。如果用户明确知晓函数的语义,可以通过将会话变量 enable_ext_func_pred_pushdown 设为 true 来开启函数下推功能。

同时,不同的数据源有不同的函数黑名单,黑名单中的函数不会被下推。

数据源函数黑名单说明
MySQL- DATE_TRUNC
- MONEY_FORMAT
- NEGTIVE
MySQL 还可以通过 FE 配置项 jdbc_mysql_unsupported_pushdown_functions 来设置额外的黑名单,如:jdbc_mysql_unsupported_pushdown_functions==func1,func2
Clickhouse- FROM_UNIXTIME
- UNIX_TIMESTAMP
Oracle- NVL
- IFNULL

行数限制

如果在查询中带有 limit 关键字,Doris 会将 limit 下推到数据源,以减少数据传输量。可以通过 EXPLAIN 语句查看生成的 SQL 中是否包含 LIMIT 子句确认。

写入操作

Doris 支持将数据通过 JDBC 协议写回到对应的数据源。

INSERT INTO mysql_table SELECT * FROM internal.doris_db.doris_tbl;

语句透传

适用场景

Doris 支持通过透传的方式,直接在 JDBC 数据源中执行对应的 DDL、DML 语句和查询语句。该功能适用于以下场景:

  • 提升复杂查询性能

    默认情况下,Doris 查询优化器会对原始 SQL 进行解析,根据一定规则生成需要发送给数据源的 SQL。这个 SQL 通常是单表简单查询,无法生成聚合、关联查询等算子。比如如下查询:

    SELECT smallint_u, sum(int_u)
    FROM all_types WHERE smallint_u > 10 GROUP BY smallint_u;

    最终生成的 SQL 为:

    SELECT smallint_u, int_u 
    FROM all_types
    WHERE smallint_u > 10;

    而聚合操作是在 Doris 中完成的。因此在某些情况下,可能需要从源端通过网络读取大量数据,导致查询效率低下。通过语句透传,可以将原始 SQL 直接透传给数据源,从而使用数据源本身的计算功能完成 SQL 执行,提升查询性能。

  • 统一管理

    除了查询 SQL 外,语句透传功能也可以透传 DDL 和 DML 语句。因此,可以通过 Doris 直接对源端数据进行库表操作,如创建、删除表,修改表结构等。

透传 SQL

SELECT * FROM
QUERY(
'catalog' = 'mysql_catalog',
'query' = 'SELECT smallint_u, sum(int_u) FROM db.all_types WHERE smallint_u > 10 GROUP BY smallint_u;'
);

QUERY 表函数有两个参数:

  • catalog:Catalog 名称,需要按照 Catalog 的名称填写。

  • query:需要执行的查询语句,并且需要直接使用数据源对应的语法。

透传 DDL 和 DML

CALL EXECUTE_STMT("jdbc_catalog", "insert into db1.tbl1 values(1,2), (3, 4)");

CALL EXECUTE_STMT("jdbc_catalog", "delete from db1.tbl1 where k1 = 2");

CALL EXECUTE_STMT("jdbc_catalog", "create table dbl1.tbl2 (k1 int)");

EXECUTE_STMT() 函数有两个参数:

  • 第一个参数:Catalog 名称,目前仅支持 JDBC 类型 Catalog。

  • 第二个参数:执行语句,目前仅支持 DDL 和 DML 语句,并且需要直接使用数据源对应的语法。

使用限制

通过 CALL EXECUTE_STMT() 命令,Doris 会直接将用户编写的 SQL 语句发送给 Catalog 对应的 JDBC 数据源进行执行。因此,这个操作有如下限制:

  • SQL 语句必须是数据源对应的语法,Doris 不会做语法和语义检查。

  • SQL 语句中引用的表名建议是全限定名,即 db.tbl 这种格式。如果未指定 db,则会使用 JDBC Catalog 的 JDBC URL 中指定的 db 名称。

  • SQL 语句中不可引用 JDBC 数据源之外的库表,也不可以引用 Doris 的库表。但可以引用在 JDBC 数据源内的,但是没有同步到 Doris JDBC Catalog 的库表。

  • 执行 DML 语句,无法获取插入、更新或删除的行数,只能获取命令是否执行成功。

  • 只有对 Catalog 有 LOAD 权限的用户,才能执行CALL EXECUTE_STMT()命令。

  • 只有对 Catalog 有 SELECT 权限的用户,才能执行 query() 表函数。

  • 创建 Catalog 时使用的 JDBC 用户,需要在源端,对所执行的语句有相应的权限。

  • query() 表函数读取到的的数据,数据类型的支持与所查询的 catalog 类型支持的数据类型一致。

附录

大小写敏感设置

默认情况下,Doris 的库名和表名是大小写敏感的,而列名是大小写不敏感的,并且可以通过参数配置就行修改。同时,一些 JDBC 数据源的库名、表名、列名的大小写敏感规则和 Doris 的规则不一致,这会导致在通过 JDBC Catalog 进行名称映射时,可能会出现名称冲突等问题。这里介绍下如何解决此类问题。

显示名称与查询名称

在 Doris 中,一个对象名称(下面我们以表名称指代)可以分为【显示名称】和【使用名称】。比如对于表名,【显示名称】是指通过 SHOW TABLES 看到的名称。而【查询名称】是指通过可以在 SELECT 语句总使用的名称。

比如,一个表的真实名称是 MyTable。通过修改 FE 的配置项 lower_case_table_names,这个表名的【显示名称】和【查询名称】会有所区别:

配置项说明真实名称显示名称查询名称
lower_case_table_names=0默认配置。使用原始名称存储和显示,查询时大小写敏感MyTableMyTable查询时大小写敏感,必须使用:MyTable
lower_case_table_names=1使用小写名称存储和显示,查询时大小写不敏感MyTablemytable查询时大小写不敏感,比如可以使用 MyTablemytable
lower_case_table_names=2使用原始名称存储和显示,查询时大小写不敏感MyTableMyTable查询时大小写不敏感,比如可以使用 MyTablemytable

JDBC Catalog 名称大小写规则

Doris 本身只可以配置【表名】大小写的规则。而 JDBC Catalog 需要额外处理【库名】和【列名】。因此,我们使用额外的 Catalog 属性 lower_case_meta_names 来配合 lower_case_table_names 一起使用。

配置项说明
lower_case_meta_names创建 Catalog 时通过 properties 指定,作用于该 Catalog。默认为 false。当设置为 true 时,Doris 会将库、表、列名全部转换为小写存储并显示,查询时需使用 Doris 中的小写名称。
lower_case_table_namesFE 配置项,在 fe.conf 中配置,作用范围为整个集群。默认为 0

注:若 lower_case_meta_names = true,则此时不会参考 lower_case_table_names,一律将库名、表名、列名都转换为小写。

根据 lower_case_meta_names(true/false) 和 lower_case_table_names(0/1/2)的不同组合,库名、表名、列名在 存储时查询时 的表现方式如下表所示(“原始”表示保持外部数据源的大小写,“小写”表示自动转换为小写,“任意大小写”表示查询时可随意使用大小写):

lower_case_table_names & lower_case_meta_namesDatabase 显示名称Table 显示名称Column 显示名称Database 查询名称Table 查询名称Column 查询名称
0 & false原始原始原始原始原始任意大小写
0 & true小写小写小写小写小写任意大小写
1 & false原始小写原始原始任意大小写任意大小写
1 & true小写小写小写小写任意大小写任意大小写
2 & false原始原始原始原始任意大小写任意大小写
2 & true小写小写小写小写任意大小写任意大小写

大小写冲突检查

在通过 JDBC Catalog 进行名称映射时,可能会出现名称冲突。比如源端列名称大小写敏感,存在 IDid 两列。如果设置了 lower_case_meta_names = true,则这两个列在映射成小写后会产生冲突。Doris 会根据以下规则进行冲突检查:

  • 任意场景下,Doris 都会检查【列名】的大小写冲突(如 idID 是否同时存在)。

  • lower_case_meta_names = true 时,Doris 会检查库名、表名、列名是否存在大小写冲突(如 DORISdoris 同时存在)。

  • lower_case_meta_names = falselower_case_table_names12 时,Doris 会检查【表名】是否冲突(如 ordersORDERS)。

  • lower_case_table_names = 0 时,库名、表名均大小写敏感,无需额外转换。

大小写冲突的解决方案

当出现冲突时,Doris 会报错,须通过以下方式解决冲突。

对于存在仅大小写不同而重复的库、表、列(例如 DORISdoris)导致 Doris 无法正常区分的情况,可通过为 Catalog 设置 meta_names_mapping 来指定手动映射,从而解决冲突。

配置示例:

{
"databases": [
{
"remoteDatabase": "DORIS",
"mapping": "doris_1"
},
{
"remoteDatabase": "doris",
"mapping": "doris_2"
}
],
"tables": [
{
"remoteDatabase": "DORIS",
"remoteTable": "DORIS",
"mapping": "doris_1"
},
{
"remoteDatabase": "DORIS",
"remoteTable": "doris",
"mapping": "doris_2"
}
],
"columns": [
{
"remoteDatabase": "DORIS",
"remoteTable": "DORIS",
"remoteColumn": "DORIS",
"mapping": "doris_1"
},
{
"remoteDatabase": "DORIS",
"remoteTable": "DORIS",
"remoteColumn": "doris",
"mapping": "doris_2"
}
]
}

驱动包安全性

驱动包由用户上传到 Doris 集群,因此存在一定安全隐患。用户可以通过以下方式进行安全加固。

  1. Doris 认为 jdbc_drivers_dir 目录下的所有驱动包都是安全的,不会对其进行路径检查。管理员需自行管理这个目录下的文件以确保其安全性。

  2. 如使用本地路径或 HTTP 路径指定的驱动包,Doris 会做如下检查:

    • 通过 FE 配置项 jdbc_driver_secure_path 来控制允许的驱动包路径。该配置项可配置多个路径,以分号分隔。当配置了该项时,Doris 会检查 driver_url 的路径前缀是否在 jdbc_driver_secure_path 中,如果不在其中,则会拒绝创建。

      示例:

      如配置 jdbc_driver_secure_path = "file:///path/to/jdbc_drivers;http://path/to/jdbc_drivers" ,则只允许以 file:///path/to/jdbc_drivershttp://path/to/jdbc_drivers 开头的驱动包路径。

    • 此参数默认为 * 。如果为空或者 *,表示允许所有路径的 Jar 包。

  3. 在创建数据目录时,可以通过 checksum 参数来指定驱动包的校验和。Doris 会在加载驱动包后,对驱动包进行校验,如果校验失败,则会拒绝创建。

连接池清理

在 Doris 中,每个 FE 和 BE 节点都会维护一个连接池,这样可以避免频繁地打开和关闭单独的数据源连接。连接池中的每个连接都可以用来与数据源建立连接并执行查询。任务完成后,这些连接会被归还到池中以便重复使用,这不仅提高了性能,还减少了建立连接时的系统开销,并帮助防止达到数据源的连接数上限。

可以根据实际情况调整连接池的大小,以便更好地适应不同工作负载。通常情况下,连接池的最小连接数应该设置为 1,以确保在启用保活机制时至少有一个连接处于活跃状态。连接池的最大连接数应该设置为一个合理的值,以避免过多的连接占用资源。

同时为了避免在 BE 上累积过多的未使用的连接池缓存,可以通过设置 BE 的 jdbc_connection_pool_cache_clear_time_sec 参数来指定清理缓存的时间间隔。默认值为 28800 秒(8 小时),此间隔过后,BE 将强制清理所有超过该时间未使用的连接池缓存。

凭证更新

使用 JDBC Catalog 连接外部数据源时,需谨慎更新数据库凭证。

Doris 通过连接池维持活跃连接以快速响应查询。但凭证变更后,连接池可能会继续使用旧凭证尝试建立新连接并失败。由于系统试图保持一定数量的活跃连接,这种错误尝试会重复执行,且在某些数据库系统中,频繁的失败可能导致账户被锁定。

建议在必须更改凭证时,同步更新 Doris JDBC Catalog 配置,并重启 Doris 集群,以确保所有节点使用最新凭证,防止连接失败和潜在的账户锁定。

可能遇到的账户锁定如下:

MySQL: account is locked
Oracle: ORA-28000: the account is locked
SQL Server: Login is locked out

连接池问题排查

  1. HikariPool 获取连接超时错误 Connection is not available, request timed out after 5000ms

    • 可能的原因

      • 原因 1:网络问题(例如,服务器不可达)

      • 原因 2:身份认证问题,例如无效的用户名或密码

      • 原因 3:网络延迟过高,导致创建连接超过 5 秒超时时间

      • 原因 4:并发查询过多,超过了连接池配置的最大连接数

    • 解决方案

      • 如果只有 Connection is not available, request timed out after 5000ms 这一类错误,请检查 原因 3原因 4

        • 检查是否存在网络延迟过高或资源耗尽的情况。

        • 调大连接池的最大连接数:

        ALTER CATALOG catalog_name SET PROPERTIES ('connection_pool_max_size' = '100');
        • 调大连接超时时间:
        ALTER CATALOG catalog_name SET PROPERTIES ('connection_pool_max_wait_time' = '10000');
      • 如果除了 Connection is not available, request timed out after 5000ms 之外还有其他错误信息,请检查这些附加错误:

        • 网络问题(例如,服务器不可达)可能导致连接失败。请检查网络连接是否正常。

        • 身份认证问题(例如,用户名或密码无效)也可能导致连接失败。请检查配置中使用的数据库凭据,确保用户名和密码正确无误。

        • 根据具体错误信息,调查与网络、数据库或身份认证相关的问题,找出根本原因。