29 データベース・シャーディングのJDBCによるサポート

この項では、データベース・シャーディング機能に対するOracle JDBCのサポートについて説明します。

29.1 JDBCユーザー用のデータベース・シャーディングの概要

現在、Webアプリケーションには大量のデータのスケーラビリティに関する新しい課題があります。この問題に共通して受け入れられているソリューションはシャーディングです。シャーディングは、独立したデータベース間でデータが水平にパーティション化されるデータ階層アーキテクチャです。このような構成内の各データベースをシャードと呼びます。

すべてのシャードが集まったものが単一の論理データベースで、これはGlobally Distributed Databaseと呼ばれます。シャードはCPU、メモリー、記憶域デバイスなどの物理リソースを共有しないため、シャーディングは何も共有しないデータベース・アーキテクチャです。

シャーディングはグローバル・データ・サービス(GDS)を使用します。GDSは可用性、負荷、ネットワーク待機時間およびレプリケーション・ラグに基づいて、クライアント・リクエストを適切なデータベースにルーティングします。GDSプールは、レプリケートされたデータベースのセットであり、同じグローバル・サービスを提供します。GDSプールのデータベースは、異なるリージョンにある複数のデータ・センターに配置できます。シャードされたGDSプールには、Oracle Globally Distributed Databaseのすべてのシャードとそのレプリカが含まれ、データベース・クライアントには1つのGlobally Distributed Databaseと認識されます。

Oracle Database 12cリリース2 (12.2.0.1)以降、Oracle JDBCではデータベース・シャーディングがサポートされています。JDBCドライバは指定されたシャーディング・キーおよびスーパー・シャーディング・キーを認識し、データが含まれている該当シャードに接続します。シャードに対する接続が確立されると、DML、SQL問合せなどのサポートされているデータベース操作は通常どおりに実行されます。

29.2 シャーディング・キーを使用したJDBC接続の作成

シャード認識アプリケーションでは、Oracle Globally Distributed Databaseへの接続の確立に必要なシャーディング・キーおよびスーパー・シャーディング・キーを識別して作成する必要があります。

例29-1 シャーディング・キーの作成

次の例は、シャーディング・キーの作成方法を示します。

import java.sql.Connection;
import java.sql.JDBCType;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ShardingKey;
import java.sql.Statement;

import oracle.jdbc.pool.OracleDataSource;

public class JDBCShardingExample {

    public static void main(String[] args) throws Exception {

        OracleDataSource ods = new OracleDataSource();
        ods.setURL("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(HOST=myhost)(PORT=<gsm-listener-port>)
            (PROTOCOL = tcp))(CONNECT_DATA = (SERVICE_NAME = gsmservicename)))
    ");
    ods.setUser(<db_user_name>);
    ods.setPassword(<db_password>);

    int empId = 1234;
    String location = "US";

    // Employee ID is the sharding key column
    ShardingKey shardingKey = ods.createShardingKeyBuilder()
        .subkey(empId, JDBCType.INTEGER)
        .build();

    // Employee location is the super sharding key column
    ShardingKey superShardingKey = ods.createShardingKeyBuilder()
        .subkey(location, JDBCType.VARCHAR)
        .build();

    // Get a direct connection to the shard that contains the record of the employee
    // with employee id = 1234 and location="US", using sharding key and super sharding key
    
    try (Connection connection = ods.createConnectionBuilder()
        .shardingKey(shardingKey)
        .superShardingKey(superShardingKey)
        .build()) {

              PreparedStatement pst = connection.prepareStatement("select * from employee where emp_id=? and location=?");
              pst.setInt(1, 1234);
              pst.setString(2, "US");
              ResultSet rs = pst.executeQuery();
              // Retrieve the employee details using resultset
              rs.close();
              pst.close();
          }      
      }

}

ノート:

  • 有効でサポートされているデータ型の固定のセットがあります。サポートされていないデータ型をキーとして使用すると、例外が発生します。次のリストに、サポートされているデータ型を示します。

    • OracleType.VARCHAR2/JDBCType.VARCHAR

    • OracleType.CHAR/JDBCType.CHAR

    • OracleType.NVARCHAR/JDBCType.NVARCHAR

    • OracleType.NCHAR/JDBCType.NCHAR

    • OracleType.NUMBER/JDBCType.NUMERIC

    • OracleType.FLOAT/ JDBCType.FLOAT

    • OracleType.DATE/ JDBCType.DATE

    • OracleType.TIMESTAMP/JDBCType.TIMESTAMP

    • OracleType.TIMESTAMP_WITH_LOCAL_TIME_ZONE

    • OracleType.RAW

  • データベースで指定されているNLS書式に準拠したシャーディング・キーを指定する必要があります。

29.3 シャーディング・データソースの概要

Oracle Databaseリリース21c以降、JDBC Thinドライバでは、シャーディング・キーを設定せずにOracle Globally Distributed DatabaseへのJava接続を確立できます。この場合、接続を確立するためにシャーディング・キーおよびスーパー・シャーディング・キーを識別して作成する必要はありません。

シャーディング機能を備えたoracle.jdbc.pool.OracleDataSourceでは、アプリケーション・コードが変更されないため、Oracle Globally Distributed Databaseに透過的にスケール・アウトされます。シャーディング・キーをSQLまたはPL/SQLから導出できる場合、JDBCドライバは、アプリケーションがシャーディング・キーを送信しなくてもキーを識別できます。

この機能を使用するには、接続プロパティOracleConnection.CONNECTION_PROPERTY_USE_SHARDING_DRIVER_CONNECTIONtrueに設定する必要があります。この接続プロパティのデフォルト値はfalseです。

29.3.1 例: シャーディング・データソースの使用方法

この項の例では、シャーディング・データソースの使用方法を示します。

例29-2 シャーディング・データソースの使用

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import javax.sql.DataSource;

import oracle.jdbc.internal.OracleConnection;
import oracle.jdbc.pool.OracleDataSource;

public class ShardingDataSourceSample {
      public static void main(String[] args) throws SQLException {
            ShardingDataSourceSample sample = new ShardingDataSourceSample();
            DataSource ds = sample.getDataSource();
            // get the details of following customers
            int[] customerIds = new int[] { 100, 101, 102, 103, 104, 105 };
            try (Connection conn = ds.getConnection()) {
                  for (int id : customerIds) {
                        sample.displayCustomerDetails(conn, id);
                  }
                  System.out.println(((OracleConnection) conn).getPercentageQueryExecutionOnDirectShard());
            }
      }

      private void displayCustomerDetails(Connection conn, int id) throws SQLException {
            try (PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM CUSTOMER where ID = ?")) {
                  pstmt.setInt(1, id);
                  try (ResultSet rs = pstmt.executeQuery()) {
                        while (rs.next()) {
                              // print the customer details
                        }
                  }
            }
      }

      private DataSource getDataSource() throws SQLException {
        OracleDataSource ds = new OracleDataSource();
    ds.setURL(<gsmURL>);
    ds.setUser(<userName>);
    ds.setPassword(<password>);
    Properties prop = new Properties();
    // Connection property to enable sharding data source feature
    prop.setProperty(OracleConnection.CONNECTION_PROPERTY_USE_SHARDING_DRIVER_CONNECTION, "true");
    ds.setConnectionProperties(prop);
    return ds;
  }
}

29.4 シャーディング・データソースの利点

新しいシャーディング・データソースの利点は、次のとおりです。

  • シャーディングAPIを使用してシャーディング・キーを渡す必要はありません。シャーディング・データソースはSQL文からシャーディング・キーを導出するためです。
  • ユニバーサル接続プール(UCP)を構成する必要はありません。シャーディング・データソースがUCPの自動チューニング機能を使用するためです。
  • 新しいシャーディング・キーごとに物理接続をチェックインまたはチェックアウトする必要はありません。シャーディング・データソースが自動的に実行するためです。
  • クロスシャード文を単一のシャード文から分離して、それらの接続プールを個別に作成する必要はありません。シャーディング・データソースがこれらの接続プールを維持するためです。
  • シャーディング・データソースにより、プリペアド文のキャッシュが可能になり、SQL文で使用されたキーに基づいてダイレクト・シャードに接続がルーティングされます。
  • シャーディング・データソースにより、アプリケーションが簡素化され、コードを変更することなくアプリケーションのパフォーマンスが最適化されます。

29.5 シャーディング・データソースの制限

この項では、シャーディング・データソースの制限について説明します。

  • シャーディング・データソースは、JDBC Thinドライバのみをサポートしています。JDBC OCIドライバまたはKPRBドライバはサポートしていません。
  • シャーディング・データソースは、ダイレクト・パス・ロード、JDBC Dynamic Monitoring Service (DMS)メトリックなどの一部のOracle JDBC拡張APIをサポートしていません。
  • シャーディング・データソースは、カタログ・データベースを介してのみPL/SQL実行をサポートしています。
  • AUTO COMMITOFFに設定されている場合、実行は常にカタログ・データベースで行われます。
  • データソース・プロパティsingleShardTransactionSupportTRUEに設定されている場合、AUTO COMMITOFFに設定されていると、シャーディング・データソースは単一のシャードに対するローカル・トランザクションをサポートします。

    次のコード・スニペットは、singleShardTransactionSupportプロパティを設定する方法を示しています。

例29-3 単一シャード・トランザクションのサポート

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import oracle.jdbc.internal.OracleConnection;
import oracle.jdbc.pool.OracleDataSource; 

public class SingleShardTransaction {
       public static void main(String[] args) throws SQLException {
             SingleShardTransaction sample = new SingleShardTransaction();
             DataSource ds = sample.getDataSource();
             // Insert and update the details of following customers in a single transaction
             int[] customerIds = new int[] { 100, 101, 102, 103, 104, 105 };

             try (Connection conn = ds.getConnection()) {
                    conn.setAutoCommit(false);
                    for (int id : customerIds) {
                          sample.insertCustomerDetails(conn, id);
                          sample.displayCustomerDetails(conn, id);
                          sample.updateCustomerDetails(conn, id);
                          sample.displayCustomerDetails(conn, id);
                          conn.commit();
                    }

                    System.out.println(((OracleConnection) conn).getPercentageQueryExecutionOnDirectShard());
             }
       } 

       private void insertCustomerDetails(Connection conn, int id) throws SQLException {
             String sql = "insert into CUSTOMER values(?, ?, ?, ?)";

             try (PreparedStatement ps = conn.prepareStatement(sql)) {
                    ps.setInt(1, id);
                    ps.setString(2, name);
                    ps.setString(3, email);
                    ps.setString(4, phoneNumber);
                    ps.executeUpdate();
             }
       }

       private void updateCustomerDetails(Connection conn, int id) throws SQLException {
             String sql = "UPDATE CUSTOMER SET name = ?, email = ?, phoneNumber = ? WHERE customerId = ?";

             try (PreparedStatement ps = conn.prepareStatement(sql)) {
                    ps.setString(1, name);
                    ps.setString(2, email);
                    ps.setString(3, phoneNumber);
                    ps.setInt(4, id);
                    ps.executeUpdate();
             }
       }

       private void displayCustomerDetails(Connection conn, int id) throws SQLException {

             try (PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM CUSTOMER where ID = ?")) {
                    pstmt.setInt(1, id);

                    try (ResultSet rs = pstmt.executeQuery()) {
                          while (rs.next()) {
                                 // Print the customer details
                          }
                    }
             }
       }

       private DataSource getDataSource() throws SQLException  {
          OracleDataSource ds = new OracleDataSource();
          ds.setURL(<gsmURL>);
          ds.setUser(<userName>);
          ds.setPassword(<password>);
          Properties prop = new Properties();
          // Connection property to enable sharding data source feature
          prop.setProperty(OracleConnection.CONNECTION_PROPERTY_USE_SHARDING_DRIVER_CONNECTION, "true");

          // Connection property to enable single shard transaction support. If this property is not set,
          // by default all the transactions are started on catalog DB. When setting this property value
          // to "true", applications must ensure that all the transactions span over a single shard only.

          prop.setProperty(oracle.jdbc.OracleConnection.CONNECTION_PROPERTY_ALLOW_SINGLE_SHARD_TRANSACTION_SUPPORT, "true");
          ds.setConnectionProperties(prop);
          return ds;
  }
}