这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

创建和配置连接

Java 应用程序必须先创建连接才能与 Vertica 交互。使用 JDBC 连接到 Vertica 与连接到大多数其他数据库相似。

导入 SQL 包

创建连接之前,您必须导入 Java SQL 包。执行此操作的一种简单方法是使用通配符导入整个包:

import java.sql.*;

您可能还需要导入 Properties 类。在将连接实例化时,您可以使用此类的实例来传递连接属性,而不必将所有内容都编码到连接字符串中:

import java.util.Properties;

如果应用程序需要在 Java 5 JVM 中运行,它将使用符合 JDBC 3.0 的较旧版本驱动程序。此驱动程序要求使用 Class.forName() 方法手动加载 Vertica JDBC 驱动程序:

// Only required for old JDBC 3.0 driver
try {
    Class.forName("com.vertica.jdbc.Driver");
} catch (ClassNotFoundException e) {
    // Could not find the driver class. Likely an issue
    // with finding the .jar file.
    System.err.println("Could not find the JDBC driver class.");
    e.printStackTrace();
    return; // Exit. Cannot do anything further.
}

应用程序可能在 Java 6 或更高版本的 JVM 中运行。如果是这样,则 JVM 会自动加载兼容 Vertica JDBC 4.0 的驱动程序,而不要求调用 Class.forName。但是,发出此调用不会对进程产生负面影响。因此,如果您希望应用程序同时与 Java 5 和 Java 6(或更高版本)JVM 兼容,应用程序仍可以调用 Class.forName

打开连接

导入 SQL 包之后,您便已准备好通过调用 DriverManager.getConnection() 方法来创建连接。您至少必须向此方法提供以下信息:

  • 数据库群集中的节点的 IP 地址或主机名。

    您可以提供 IPv4 地址、IPv6 地址或主机名。

    在 IPv4/IPv6 混合网络中,DNS 服务器配置决定了哪个 IP 版本地址最先发送。可使用 PreferredAddressFamily 选项来强制连接使用 IPv4 或 IPv6。

  • 数据库的端口号

  • 数据库的名称

  • 数据库用户帐户的用户名

  • 用户的密码(如果该用户具有密码)

前三个参数始终作为连接字符串的一部分提供,连接字符串是一个 URL,可指示 JDBC 驱动程序从何处查找数据库。连接字符串的格式为:

"jdbc:vertica://VerticaHost:portNumber/databaseName"

此连接字符串的第一部分选择了 Vertica JDBC 驱动程序,后跟数据库的位置。

可以通过以下三种方法之一向 JDBC 驱动程序提供后两个参数(用户名和密码):

  • 作为连接字符串的一部分。将以 URL 参数的相似方式对这两个参数进行编码:

    "jdbc:vertica://VerticaHost:portNumber/databaseName?user=username&password=password"
    
  • 作为单独的参数传递到 DriverManager.getConnection()

    Connection conn = DriverManager.getConnection(
            "jdbc:vertica://VerticaHost:portNumber/databaseName",
            "username", "password");
    
  • Properties 对象中:

    Properties myProp = new Properties();
    myProp.put("user", "username");
    myProp.put("password", "password");
    Connection conn = DriverManager.getConnection(
        "jdbc:vertica://VerticaHost:portNumber/databaseName", myProp);
    

对以上三种方法来说,Properties 对象是最灵活的,因为使用此对象可以轻松地将其他连接属性传递到 getConnection() 方法。有关其他连接属性的详细信息,请参阅连接属性设置和获取连接属性值

如果建立与数据库的连接时出现任何问题,getConnection() 方法将在其子类之一上引发 SQLException。若要防止异常,请将方法包含在 try-catch 块中,如以下有关建立连接的完整示例所示。

import java.sql.*;
import java.util.Properties;

public class VerySimpleVerticaJDBCExample {
    public static void main(String[] args) {
        /*
         * If your client needs to run under a Java 5 JVM, It will use the older
         * JDBC 3.0-compliant driver, which requires you manually load the
         * driver using Class.forname
         */
        /*
         * try { Class.forName("com.vertica.jdbc.Driver"); } catch
         * (ClassNotFoundException e) { // Could not find the driver class.
         * Likely an issue // with finding the .jar file.
         * System.err.println("Could not find the JDBC driver class.");
         * e.printStackTrace(); return; // Bail out. We cannot do anything
         * further. }
         */
        Properties myProp = new Properties();
        myProp.put("user", "dbadmin");
        myProp.put("password", "vertica");
        myProp.put("loginTimeout", "35");
        myProp.put("KeystorePath", "c:/keystore/keystore.jks");
     myProp.put("KeystorePassword", "keypwd");
     myProp.put("TrustStorePath", "c:/truststore/localstore.jks");
     myProp.put("TrustStorePassword", "trustpwd");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                    "jdbc:vertica://V_vmart_node0001.example.com:5433/vmart", myProp);
            System.out.println("Connected!");
            conn.close();
        } catch (SQLTransientConnectionException connException) {
            // There was a potentially temporary network error
            // Could automatically retry a number of times here, but
            // instead just report error and exit.
            System.out.print("Network connection issue: ");
            System.out.print(connException.getMessage());
            System.out.println(" Try again later!");
            return;
        } catch (SQLInvalidAuthorizationSpecException authException) {
            // Either the username or password was wrong
            System.out.print("Could not log into database: ");
            System.out.print(authException.getMessage());
            System.out.println(" Check the login credentials and try again.");
            return;
        } catch (SQLException e) {
            // Catch-all for other exceptions
            e.printStackTrace();
        }
    }
}

使用密钥库和信任库创建连接

您可以使用密钥库和信任库创建与 JDBC 客户端驱动程序的安全连接。有关 Vertica 中的安全性的详细信息,请参阅安全性和身份验证

有关如何在 Vertica 中生成(或导入外部)证书的示例和说明,请参阅生成 TLS 证书和密钥

要在 Vertica 中查看您的密钥和证书,请参阅CERTIFICATESCRYPTOGRAPHIC_KEYS

  1. 生成您自己的自签名证书或使用现有 CA(证书颁发机构)证书作为根 CA。有关此过程的信息,请参考 Schannel 文档

  2. 可选:生成或导入由根 CA 签署的中间 CA 证书。虽然中间 CA 证书不是必需的,但拥有中间 CA 证书对于测试和调试连接很有用。

  3. 为 Vertica 生成并签署(或导入)服务器证书。

  4. 使用 ALTER TLS CONFIGURATION 将 Vertica 配置为使用客户端/服务器 TLS 进行新连接。有关详细信息,请参阅配置客户端-服务器 TLS

    对于服务器模式(无客户端证书验证):

    => ALTER TLS CONFIGURATION server TLSMODE 'ENABLE';
    => ALTER TLS CONFIGURATION server CERTIFICATE server_cert;
    

    对于相互模式(根据 TLSMODE 进行不同严格性的客户端证书验证):

    => ALTER TLS CONFIGURATION server TLSMODE 'TRY_VERIFY';
    => ALTER TLS CONFIGURATION server CERTIFICATE server_cert ADD CA CERTIFICATES ca_cert;
    
  5. 或者,您可以使用 CREATE AUTHENTICATION 禁用所有非 SSL 连接。

    => CREATE AUTHENTICATION no_tls METHOD 'reject' HOST NO TLS '0.0.0.0/0';
    => CREATE AUTHENTICATION no_tls METHOD 'reject' HOST NO TLS '::/128';
    
  6. 使用签署服务器证书的同一 CA 为您的客户端生成和签署证书。

  7. 将 pem 证书链转换为单个 pkcs 12 文件。

  8. 将客户端密钥和链从 pkcs12 文件导入到密钥库 JKS 文件中。有关使用 keytool 命令界面的信息,请参考 Java 文档

    $ keytool -importkeystore -srckeystore -alias my_alias -srcstoretype PKCS12 -srcstorepass my_password -noprompt -deststorepass my_password -destkeypass my_password -destkeystore /tmp/keystore.jks
    
  9. 将 CA 导入到信任库 JKS 文件中。

    $ keytool -import -file certs/intermediate_ca.pem -alias my_alias -trustcacerts -keystore /tmp/truststore.jks -storepass my_truststore_password -noprompt
    

用法注意事项

  • 断开用户会话的连接时,任何未提交的事务会自动回退。

  • 如果数据库不符合 Vertica 许可条款,则 Vertica 会在您建立与数据库的连接时发出 SQLWarning。您可以使用 Connection.getWarnings() 方法检索此警告。有关遵守许可条款的详细信息,请参阅管理许可证

1 - JDBC 连接属性

可以使用连接属性来配置 JDBC 客户端应用程序和 Vertica 数据库之间的连接。属性提供有关连接的基本信息,例如,用来连接到数据库的服务器名称和端口号。您还可以使用属性来调整连接的性能以及启用日志记录。

可以采用以下方式之一设置连接属性:

  • 将属性名称和值包括在传递给方法 DriverManager.getConnection() 的连接字符串中。

  • Properties 对象中设置属性,然后将其传递给方法 DriverManager.getConnection()

  • 使用方法 VerticaConnection.setProperty()。使用此方法,您只能更改那些在创建连接后仍可更改的连接属性。

此外,一些标准 JDBC 连接属性在 Connection 界面上具有 getter 和 setter,例如 Connection.setAutoCommit()

连接属性

您只能在打开与数据库的连接之前设置下表中的属性。其中两个属性对每个连接都是必需的。

OAuth 连接属性

以下连接属性与 JDBC 中的 OAuth 相关。

超时属性

使用以下参数,您可以为每个步骤指定各种超时以及 JDBC 与 Vertica 数据库的整体连接。

下图说明了这些属性之间的关系以及它们在 JDBC 尝试连接到 Vertica 数据库时所起的作用: 关于当 JDBC 尝试连接到一个主节点和两个备份节点时超时表现行为的图表

常规属性

可以在建立连接之后设置以下属性。这些属性全都不是必需的。

日志记录属性

必须在打开连接之前设置这些控制客户端日志记录的属性。这些属性全都不是必需的,您无法在 Connection 对象已实例化之后更改所有这些属性。

Kerberos 连接参数

可以使用以下参数为使用 Kerberos 的客户端身份验证设置服务名称主体和主机名主体。

可路由连接 API 连接参数

可以使用以下参数来设置属性,以便为连接启用和配置可路由连接查找。

有关操作这些属性的信息,请参阅设置和获取连接属性值

2 - 设置和获取连接属性值

可以采用以下方式之一设置连接属性:

  • 将属性名称和值包括在传递给方法 DriverManager.getConnection() 的连接字符串中。

  • Properties 对象中设置属性,然后将其传递给方法 DriverManager.getConnection()

  • 使用方法 VerticaConnection.setProperty()。使用此方法,您只能更改那些在创建连接后仍可更改的连接属性。

此外,一些标准 JDBC 连接属性在 Connection 界面上具有 getter 和 setter,例如 Connection.setAutoCommit()

在连接时设置属性

创建与 Vertica 的连接时,您可以通过以下方式设置连接属性:

  • 在连接字符串中指定这些属性。

  • 修改传递给 getConnection()Properties 对象。

连接字符串属性

您可以使用用于用户名和密码的相同 URL 参数格式在连接字符串中指定连接属性。例如,以下字符串启用 TLS 连接:

"jdbc:vertica://VerticaHost:5433/db?user=UserName&password=Password&TLSmode=require"

使用 setProperty() 方法设置主机名会替代已在连接字符串中设置的主机名。如果出现这种情况,Vertica 可能无法连接到主机。例如,如果使用以上连接字符串,以下代码会替代 VerticaHost 名称:

Properties props = new Properties();
props.setProperty("dataSource", dataSourceURL);
props.setProperty("database", database);
props.setProperty("user", user);
props.setProperty("password", password);
ps.setProperty("jdbcDriver", jdbcDriver);
props.setProperty("hostName", "NonVertica_host");

但是,如果需要新连接或替代连接,您可以在主机名属性对象中输入有效的主机名。

NonVertica_host 主机名将替代连接字符串中的 VerticaHost 名称。若要避免此问题,请将 props.setProperty("hostName", "NonVertica_host"); 行注释掉:

//props.setProperty("hostName", "NonVertica_host");

属性对象

要使用传递给 getConnection() 调用的 Properties 对象设置连接属性:

  1. 导入 java.util.Properties 类以实例化 Properties 对象。

  2. 使用 put() 方法向对象添加名称-值对。

Properties myProp = new Properties();
myProp.put("user", "ExampleUser");
myProp.put("password", "password123");
myProp.put("LoginTimeout", "35");
Connection conn;
try {
    conn = DriverManager.getConnection(
        "jdbc:vertica://VerticaHost:/ExampleDB", myProp);
} catch (SQLException e) {
    e.printStackTrace();
}

在连接后获取和设置属性

与 Vertica 建立连接后,您可以使用 VerticaConnection 方法 getProperty()setProperty() 分别设置一些连接属性的值。

使用 VerticaConnection.getProperty() 方法,您可以获取部分连接属性的值。使用此方法更改与 Vertica 建立连接后可设置的属性值。

由于这些方法特定于 Vertica,因此您必须使用以下方法之一将 Connection 对象强制转换为 VerticaConnection 接口:

  • Connection 对象导入客户端应用程序。

  • 使用完全限定的引用:com.vertica.jdbc.VerticaConnection

以下示例演示了获取和设置只读属性的值。

import java.sql.*;
import java.util.Properties;
import com.vertica.jdbc.*;

public class SetConnectionProperties {
    public static void main(String[] args) {
        // Note: If your application needs to run under Java 5, you need to
        // load the JDBC driver using Class.forName() here.
        Properties myProp = new Properties();
        myProp.put("user", "ExampleUser");
        myProp.put("password", "password123");
        // Set ReadOnly to true initially
        myProp.put("ReadOnly", "true");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                            "jdbc:vertica://VerticaHost:5433/ExampleDB",
                            myProp);
            // Show state of the ReadOnly property. This was set at the
            // time the connection was created.
            System.out.println("ReadOnly state: "
                            + ((VerticaConnection) conn).getProperty(
                                            "ReadOnly"));

            // Change it and show it again
            ((VerticaConnection) conn).setProperty("ReadOnly", false);
            System.out.println("ReadOnly state is now: " +
                             ((VerticaConnection) conn).getProperty(
                                             "ReadOnly"));
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

运行以上示例后,将在标准输出上输出以下内容:

ReadOnly state: true
ReadOnly state is now: false

3 - 为 JDBC 客户端配置 TLS

要为 JDBC 客户端配置 TLS:

设置密钥库/信任库属性

可以通过以下方式设置密钥库和信任库属性,每种方式各有利弊:

  • 在驱动程序级别。

  • 在 JVM 级别。

驱动程序级别配置

如果您将 DbVizualizer 等工具用于多个连接,请使用 JDBC 连接属性配置密钥库和信任库。但是,这确实会在连接字符串中公开这些值:

  • KeyStorePath

  • KeyStorePassword

  • TrustStorePath

  • TrustStorePassword

例如:

Properties props = new Properties();
props.setProperty("KeyStorePath", keystorepath);
props.setProperty("KeyStorePassword", keystorepassword);
props.setProperty("TrustStorePath", truststorepath);
props.setProperty("TrustStorePassword", truststorepassword);

JVM 级别配置

在 JVM 级别设置密钥库和信任库参数会将它们从连接字符串中排除,这可能更适合安全要求较严格的环境:

  • javax.net.ssl.keyStore

  • javax.net.ssl.trustStore

  • javax.net.ssl.keyStorePassword

  • javax.net.ssl.trustStorePassword

例如:

System.setProperty("javax.net.ssl.keyStore","clientKeyStore.key");
System.setProperty("javax.net.ssl.trustStore","clientTrustStore.key");
System.setProperty("javax.net.ssl.keyStorePassword","new_keystore_password")
System.setProperty("javax.net.ssl.trustStorePassword","new_truststore_password");

设置 TLSmode 连接属性

您可以设置 TLSmode 连接属性以确定如何处理证书。默认情况下,禁用 TLSmode。

TLSmode 标识 Vertica 应用于 JDBC 连接的安全级别。必须将 Vertica 配置为处理 TLS 连接,然后才能与其建立 TLS 加密连接。有关详细信息,请参阅TLS 协议。有效值包括:

  • disable:JDBC 使用纯文本连接并且不实施任何安全措施。

  • require:JDBC 使用 TLS 连接而不验证 CA 证书。

  • verify-ca:JDBC 使用 TLS 连接并确认服务器证书已由证书颁发机构签名。此设置等效于已弃用的 ssl=true 属性。

  • verify-full:JDBC 使用 TLS 连接,确认服务器证书已由证书颁发机构签名,并验证主机名与服务器证书中提供的名称匹配。

如果设置了此属性和 SSL 属性,则此属性优先。

例如,要将 JDBC 配置为使用 TLS 连接服务器而不验证 CA 证书,您可以通过 VerticaConnection.setProperty() 方法将 TLSmode 属性设置为 'require':

Properties props = new Properties();
props.setProperty("TLSmode", "verify-full");

运行 SSL 调试实用程序

配置 TLS 后,您可以为调试实用程序运行以下命令:

$ java -Djavax.net.debug=ssl

通过调试实用程序,可以使用多个调试指示符(选项)。指示符有助于缩小返回的调试信息的范围。例如,可以指定其中一个选项来打印握手消息或会话活动。

有关该调试实用程序及其选项的信息,请参阅 Oracle 文档《JSSE 参考指南》中的“调试实用程序”。

有关解释调试信息的说明,请参阅 Oracle 文档调试 SSL/TLS 连接

4 - 设置和返回客户端连接标记

JDBC 客户端具有用于设置和返回客户端连接标签的方法:getClientInfo() 和 setClientInfo()。这些方法可与 SQL 函数 GET_CLIENT_LABELSET_CLIENT_LABEL 配合使用。

使用这两种方法时,请确保向 setter 和 getter 方法都传递字符串值 APPLICATIONNAME

setClientInfo() 用于创建客户端标签,而 getClientInfo() 则用于返回客户端标签:

import java.sql.*;
import java.util.Properties;

public class ClientLabelJDBC {

    public static void main(String[] args) {
        Properties myProp = new Properties();
        myProp.put("user", "dbadmin");
        myProp.put("password", "");
        myProp.put("loginTimeout", "35");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                    "jdbc:vertica://docc05.verticacorp.com:5433/doccdb", myProp);
            System.out.println("Connected!");
            conn.setClientInfo("APPLICATIONNAME", "JDBC Client - Data Load");
            System.out.println("New Conn label: " + conn.getClientInfo("APPLICATIONNAME"));
            conn.close();
        } catch (SQLTransientConnectionException connException) {
            // There was a potentially temporary network error
            // Could automatically retry a number of times here, but
            // instead just report error and exit.
            System.out.print("Network connection issue: ");
            System.out.print(connException.getMessage());
            System.out.println(" Try again later!");
            return;
        } catch (SQLInvalidAuthorizationSpecException authException) {
            // Either the username or password was wrong
            System.out.print("Could not log into database: ");
            System.out.print(authException.getMessage());
            System.out.println(" Check the login credentials and try again.");
            return;
        } catch (SQLException e) {
            // Catch-all for other exceptions
            e.printStackTrace();
        }
    }
}

运行此方法时,会将以下结果打印到标准输出:

Connected!
New Conn Label: JDBC Client - Data Load

5 - 设置 JDBC 会话的区域设置

可以通过以下方法设置连接的区域设置:在打开该连接时将 SET LOCALE 语句包含到 ConnSettings 属性;或者在打开该连接之后的任何时候执行 SET LOCALE 语句。更改 Connection 对象的区域设置会影响使用该对象实例化的所有 Statement 对象。

可以通过执行 SHOW LOCALE 查询来获取区域设置。以下示例演示了使用 ConnSettings 设置区域设置以及执行语句和获取区域设置:

import java.sql.*;
import java.util.Properties;

public class GetAndSetLocale {
    public static void main(String[] args) {

     // If running under a Java 5 JVM, you need to load the JDBC driver
     // using Class.forname here

     Properties myProp = new Properties();
        myProp.put("user", "ExampleUser");
        myProp.put("password", "password123");

        // Set Locale to true en_GB on connection. After the connection
        // is established, the JDBC driver runs the statements in the
        // ConnSettings property.
        myProp.put("ConnSettings", "SET LOCALE TO en_GB");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                            "jdbc:vertica://VerticaHost:5433/ExampleDB",
                            myProp);

            // Execute a query to get the locale. The results should
            // show "en_GB" as the locale, since it was set by the
            // conn settings property.
            Statement stmt = conn.createStatement();
            ResultSet rs = null;
            rs = stmt.executeQuery("SHOW LOCALE");
            System.out.print("Query reports that Locale is set to: ");
            while (rs.next()) {
                System.out.println(rs.getString(2).trim());
            }

            // Now execute a query to set locale.
            stmt.execute("SET LOCALE TO en_US");

            // Run query again to get locale.
            rs = stmt.executeQuery("SHOW LOCALE");
            System.out.print("Query now reports that Locale is set to: ");
            while (rs.next()) {
                System.out.println(rs.getString(2).trim());
            }
            // Clean up
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

运行以上示例后,将在系统控制台上显示以下内容:

Query reports that Locale is set to: en_GB (LEN)
Query now reports that Locale is set to: en_US (LEN)

注意:

  • JDBC 应用程序使用 UTF-16 字符集编码,并负责将任何非 UTF-16 编码数据转换为 UTF-16。未能转换数据会导致发生错误或不正确地存储数据。

  • 向 Vertica 服务器传递数据时,JDBC 驱动程序会将 UTF-16 数据转换为 UTF-8,并且会将 Vertica 服务器发送的数据从 UTF-8 转换为 UTF-16。

6 - 更改事务隔离级别

通过更改事务隔离级别,您可以选择让事务以哪种方式防止来自其他事务的干扰。默认情况下,JDBC 驱动程序会匹配 Vertica 服务器的事务隔离级别。Vertica 的默认事务隔离级别是 READ_COMMITTED,这意味着在提交由某个事务进行的任何更改之前,任何其他事务无法读取这些更改。此级别可防止事务读取由稍后回退的其他事务插入的数据。

Vertica 还支持 SERIALIZABLE 事务隔离级别。此级别可锁定表,以防止其他事务更改查询的 WHERE 子句的结果。锁定表会产生性能影响,因为一次只有一个事务能够访问该表。

事务将保留其隔离级别直至其完成,即使在事务处理期间会话的隔离级别发生更改也是如此。Vertica 内部进程(例如 Tuple Mover刷新操作)以及 DDL 操作始终以 SERIALIZABLE 隔离级别运行以确保一致性。

建立了连接之后,您可以使用 Connection 对象的 setter (setTransactionIsolation()) 和 getter (getTransactionIsolation()) 更改事务隔离级别的连接属性。事务隔离属性的值是一个整数。Connection 接口定义了一些常数,这些常数有助于以更直观的方式设置值:

以下示例演示了将事务隔离级别设置为 SERIALIZABLE。

import java.sql.*;
import java.util.Properties;

public class SetTransactionIsolation {
    public static void main(String[] args) {
        Properties myProp = new Properties();
        myProp.put("user", "ExampleUser");
        myProp.put("password", "password123");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                            "jdbc:vertica://VerticaHost:5433/ExampleDB",
                            myProp);
            // Get default transaction isolation
            System.out.println("Transaction Isolation Level: "
                            + conn.getTransactionIsolation());
            // Set transaction isolation to SERIALIZABLE
            conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
            // Get the transaction isolation again
            System.out.println("Transaction Isolation Level: "
                            + conn.getTransactionIsolation());
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

运行此示例后,以下内容会输出到控制台:

Transaction Isolation Level: 2Transaction Isolation Level: 8

7 - JDBC 连接池

池数据源一组持续连接来减少在客户端和服务器之间重复打开网络连接的开销。与持续打开一个小型连接池并使其可供新请求使用相比,为每个请求打开新连接对服务器和客户端来说成本更高。当请求到达时,将为其分配该池中预先存在的连接之一。仅当该池中没有可用连接时,才会创建新连接。该请求完成后,连接将返回到池中并等待为其他请求提供服务。

Vertica JDBC 驱动程序支持 JDBC 4.0 标准中定义的连接池。如果要将基于 J2EE 的应用程序服务器与 Vertica 结合使用,该应用程序服务器应已有内置的数据池功能。唯一的要求是该应用程序服务器可与 Vertica 的 JDBC 驱动程序所实施的 PooledConnection 接口配合工作。应用程序服务器的池功能通常已针对该服务器设计用于处理的工作负载进行了适当优化。有关如何使用池连接的详细信息,请参阅应用程序服务器的文档。一般情况下,应在代码中以透明方式使用池连接,您只需打开连接,应用程序将处理池连接的详细信息。

如果不想使用应用程序服务器,或者应用程序服务器未提供与 Vertica 兼容的连接池,您可以使用第三方池库(例如,开源 c3p0 库或 DBCP 库)来实施连接池。

8 - JDBC 中的负载均衡

本机连接负载均衡

本机连接负载均衡有助于在 Vertica 数据库中的主机上分散客户端连接所带来的开销。服务器和客户端都必须启用本机连接负载均衡。如果两者者都启用了本机负载均衡,则当客户端最初连接到数据库中的主机时,此主机会从数据库中当前正在运行的主机列表中选择一个主机来处理客户端连接,并通知客户端它所选择的主机。

如果最初联系的主机没有选择自身来处理连接,则客户端会断开连接,然后打开指向第一个主机所选主机的另一个连接。指向此第二个主机的连接进程将照常进行—如果启用了 SSL,则会启动 SSL 协商,否则客户端会启动身份验证过程。有关详细信息,请参阅关于本机连接负载均衡

若要在客户端上启用本机连接负载均衡,请将 ConnectionLoadBalance 连接参数设置为 true。以下示例演示了下列操作:

  • 在启用本机连接负载均衡的情况下多次连接到数据库。

  • 从 V_MONITOR.CURRENT_SESSION 系统表中获取处理连接的节点的名称。


import java.sql.*;
import java.util.Properties;
import java.sql.*;
import java.util.Properties;

public class JDBCLoadingBalanceExample {
    public static void main(String[] args) {
        Properties myProp = new Properties();
        myProp.put("user", "dbadmin");
        myProp.put("password", "example_password123");
        myProp.put("loginTimeout", "35");
        myProp.put("ConnectionLoadBalance", "1");
        Connection conn;

        for (int x = 1; x <= 4; x++) {
            try {
                System.out.print("Connect attempt #" + x + "...");
                conn = DriverManager.getConnection(
                    "jdbc:vertica://node01.example.com:5433/vmart", myProp);
                Statement stmt = conn.createStatement();
                // Set the load balance policy to round robin before testing the database's load balancing.
                stmt.execute("SELECT SET_LOAD_BALANCE_POLICY('ROUNDROBIN');");
                // Query system to table to see what node we are connected to. Assume a single row
                // in response set.
                ResultSet rs = stmt.executeQuery("SELECT node_name FROM v_monitor.current_session;");
                rs.next();
                System.out.println("Connected to node " + rs.getString(1).trim());
                conn.close();
            } catch (SQLTransientConnectionException connException) {
                // There was a potentially temporary network error
                // Could automatically retry a number of times here, but
                // instead just report error and exit.
                System.out.print("Network connection issue: ");
                System.out.print(connException.getMessage());
                System.out.println(" Try again later!");
                return;
            } catch (SQLInvalidAuthorizationSpecException authException) {
                // Either the username or password was wrong
                System.out.print("Could not log into database: ");
                System.out.print(authException.getMessage());
                System.out.println(" Check the login credentials and try again.");
                return;
            } catch (SQLException e) {
                // Catch-all for other exceptions
                e.printStackTrace();
            }
        }
    }
}

运行上述示例得到了以下输出:


Connect attempt #1...Connected to node v_vmart_node0002
Connect attempt #2...Connected to node v_vmart_node0003
Connect attempt #3...Connected to node v_vmart_node0001
Connect attempt #4...Connected to node v_vmart_node0002

基于主机名的负载均衡

您还可以通过将单个主机名解析为多个 IP 地址来对工作负载进行负载均衡。当您为 DriverManager.getConnection() 方法指定主机名时,主机名会解析为来自每个连接的随机列出的 IP 地址。

例如,主机名 verticahost.example.cometc/hosts 中有以下条目:

192.0.2.0 verticahost.example.com
192.0.2.1 verticahost.example.com
192.0.2.2 verticahost.example.com

verticahost.example.com 指定为 DriverManager.getConnection() 的连接会随机解析为列出的 IP 地址之一。

9 - JDBC 连接故障转移

如果客户端应用程序尝试连接到 Vertica 群集中处于关闭状态的主机,则使用默认连接配置时连接尝试将会失败。此故障通常会向用户返回一个错误。用户必须等待主机恢复并重试连接,或者手动编辑连接设置以选择其他主机。

由于 Vertica 分析型数据库采用分布式架构,通常您不会关注哪个数据库主机处理客户端应用程序的连接。可以使用客户端驱动程序的连接故障转移功能来防止用户在无法访问连接设置中所指定主机的情况下收到连接错误。JDBC 驱动程序可通过几种方式让客户端驱动程序在无法访问连接参数中所指定的主机时自动尝试连接到其他主机:

  • 将 DNS 服务器配置为返回一个主机名的多个 IP 地址。在连接设置中使用此主机名时,客户端会尝试连接到 DNS 找到的第一个 IP 地址。如果位于该 IP 地址的主机无法访问,则客户端会尝试连接到第二个 IP,依此类推,直到其成功地连接到某个主机或试过所有 IP 地址为止。

  • 提供当您在连接参数中指定的主要主机无法访问时客户端驱动程序尝试连接的备份主机列表。

  • (仅限 JDBC)在尝试连接到下一个节点之前,使用特定于驱动程序的连接属性来管理超时。

在所有方法中,故障转移过程对于客户端应用程序是透明的(除了在选择使用列表故障转移方法时需指定备份主机列表之外)。如果主要主机无法访问,客户端驱动程序会自动尝试连接到其他主机。

故障转移仅适用于初次建立客户端连接的情况。如果连接断开,驱动程序不会自动尝试重新连接到数据库中的其他主机。

通常可选择上述两种故障转移方法中的一种。但它们可以一起使用。如果您的 DNS 服务器返回多个 IP 地址,并且您提供了备份主机列表,则客户端会首先尝试连接 DNS 服务器返回的所有 IP,然后再尝试连接备份列表中的主机。

DNS 故障转移方法可集中处理配置客户端故障转移。在向 Vertica 分析型数据库群集添加新节点时,可以选择通过编辑 DNS 服务器设置来将这些节点添加到故障转移列表中。所有使用 DNS 服务器连接到 Vertica 分析型数据库的客户端系统都会自动采用连接故障转移,而无需更改任何设置。但此方法需要对所有客户端用于连接到 Vertica 分析型数据库群集的 DNS 服务器具有管理访问权限。这在贵组织中可能无法实现。

使用备份服务器列表要比编辑 DNS 服务器设置更加容易。但此方法不能集中处理故障转移功能。如果更改了 Vertica 分析型数据库群集,您可能需要在每个客户端系统上更新应用程序设置。

使用 DNS 故障转移

若要使用 DNS 故障转移,您需要更改 DNS 服务器的设置,将单一主机名映射为 Vertica 分析型数据库群集中主机的多个 IP 地址。然后让所有客户端应用程序都使用该主机名连接到 Vertica 分析型数据库。

您可以选择让 DNS 服务器为主机名返回任何所需数量的 IP 地址。在小型群集中,您可以选择让其返回群集中所有主机的 IP 地址。但对于大型群集,应考虑选择返回一部分主机。否则可能会导致长时间延迟,因为客户端驱动程序尝试连接到数据库中处于关闭状态的每个主机会失败。

使用备份主机列表

若要启用基于备份列表的连接故障转移,需要在客户端应用程序的 BackupServerNode 参数中指定主机的至少一个 IP 地址或主机名。可以视情况在主机名或 IP 后面使用冒号和端口号。如果未提供端口号,驱动程序会默认使用标准 Vertica 端口号 (5433)。若要列出多个主机,请用逗号分隔这些主机。

以下示例演示了如何通过设置 BackupServerNode 连接参数来指定可尝试连接的其他主机。由于有意在连接字符串中使用了一个不存在的节点,因此初始连接失败。客户端驱动程序必须尝试通过备份主机来与 Vertica 建立连接。

import java.sql.*;
import java.util.Properties;

public class ConnectionFailoverExample {
    public static void main(String[] args) {
        // Assume using JDBC 4.0 driver on JVM 6+. No driver loading needed.
        Properties myProp = new Properties();
        myProp.put("user", "dbadmin");
        myProp.put("password", "vertica");
        // Set two backup hosts to be used if connecting to the first host
        // fails. All of these hosts will be tried in order until the connection
        // succeeds or all of the connections fail.
        myProp.put("BackupServerNode", "VerticaHost02,VerticaHost03");
        Connection conn;
        try {
            // The connection string is set to try to connect to a known
            // bnad host (in this case, a host that never existed).
            conn = DriverManager.getConnection(
                    "jdbc:vertica://BadVerticaHost:5433/vmart", myProp);
            System.out.println("Connected!");
            // Query system to table to see what node we are connected to.
            // Assume a single row in response set.
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(
                    "SELECT node_name FROM v_monitor.current_session;");
            rs.next();
            System.out.println("Connected to node " + rs.getString(1).trim());
            // Done with connection.
            conn.close();
        } catch (SQLException e) {
            // Catch-all for other exceptions
            e.printStackTrace();
        }
    }
}

运行以上示例后,将在系统控制台上生成类似于以下内容的输出:

Connected!
Connected to node v_vmart_node0002

请注意,系统会与备份列表中的第一个节点建立连接(节点 2)。

指定连接超时

LoginTimeout 控制 JDBC 与节点建立 TCP 连接并登录 Vertica 的超时时间。

LoginNodeTimeout 控制 JDBC 登录 Vertica 数据库的超时时间。在指定的超时之后,JDBC 尝试连接到由连接属性 BackupServerNode 或 DNS 解析确定的“下一个”节点。这在节点已启动的情况下很有用,但 Vertica 进程会出现问题。

LoginNetworkTimeout 控制 JDBC 与 Vertica 节点建立 TCP 连接的超时时间。如果您未设置此连接属性,且 JDBC 客户端尝试连接的节点已关闭,则 JDBC 客户端将“无限期”等待,但实际上使用的是系统默认的 70 秒超时。LoginNetworkTimeout 的一个典型用例是,如果当前 Vertica 节点因维护而停机并且无法修改 JDBC 应用程序的连接字符串,则让 JDBC 连接到另一个节点。

NetworkTimeout 控制 Vertica 在建立连接并登录到数据库后响应客户端请求的超时时间。

要在连接字符串中设置这些参数:

# LoginTimeout is 30 seconds, LoginNodeTimeout is 10 seconds, LoginNetworkTimeout is 2 seconds, NetworkTimeout is 0.5 seconds
Connection conn = DriverManager.getConnection("jdbc:vertica://VerticaHost:5433/verticadb?user=dbadmin&loginTimeout=30&loginNodeTimeout=10"&loginNetworkTimeout=2&networkTimeout=500");

要将这些参数设置为连接属性:


Properties myProp = new Properties();
myProp.put("user", "dbadmin");
myProp.put("loginTimeout", "30"); // overall connection timeout is 30 seconds to make sure it is not too small for failover
myProp.put("loginNodeTimeout", "10"); // JDBC waits 10 seconds before attempting to connect to the next node if the Vertica process is running but does not respond
myProp.put("loginNetworkTimeout", "2"); // node connection timeout is 2 seconds
myProp.put("networkTimeout", "500"); // after the client has logged in, Vertica has 0.5 seconds to respond to each request
Connection conn = DriverManager.getConnection("jdbc:vertica://VerticaHost:5433/verticadb", myProp);

与负载均衡的交互

启用本机连接负载均衡时,在 BackupServerNode 连接参数中指定的其他服务器将只用于指向 Vertica 主机的初始连接。如果主机将客户端重定向至数据库群集中的其他主机来处理其连接请求,则第二个连接将不使用备份节点列表。这很少出现问题,因为本机连接负载均衡知道数据库中的哪个节点目前正处于运行状态。

有关详细信息,请参阅JDBC 中的负载均衡