日本語PDF

8 シャード・データベースのアプリケーションの開発

シャードへの直接ルーティング

Oracleクライアントおよび接続プールは、高パフォーマンスのデータ依存ルーティングのために接続文字列に指定されたシャーディング・キーを認識できます。接続レイヤーのシャード・ルーティング・キャッシュは、データが常駐するシャードにデータベース・リクエストを直接ルーティングするために使用されます。

シャードへの直接(キーベース)ルーティングでは、必要なトランザクションに関連するデータが含まれている単一の関連するシャードに対して、シャーディング・キーを使用して接続が確立されます。

接続のチェック時にユーザー・セッション・レベルでデータベース接続リクエストをルーティングするために、シャーディング・キーが使用されます。コンポジット・シャーディング方法では、シャーディング・キーおよびスーパー・シャーディング・キーの両方が必要となります。直接(キーベース)ルーティングでは、シャーディング・キー(またはスーパー・シャーディング・キー)を接続の一部として渡す必要があります。この情報に基づいて、指定されたシャーディング・キーまたはスーパー・シャーディング・キーに関係するデータが含まれている関連するシャードに対して、接続が確立されます。

シャードとのセッションが確立されると、すべてのSQL問合せおよびDMLが該当するシャードのスコープ内でサポートされ、実行されます。このルーティングは高速であり、シャード内トランザクションを実行するすべてのOLTPワークロードで使用されます。最高のパフォーマンスおよび可用性を必要とするすべてのOLTPワークロードに直接ルーティングを使用することをお薦めします。

Oracle Shardingをサポートするために、Oracle接続プールおよびドライバに対して重要な機能強化が行われました。JDBC、UCP (Universal Connection Pool)、OCI (OCI Session Pool)およびODP.NET (Oracle Data Provider for .NET)は、接続作成中にシャーディング・キーを渡すためのAPIを提供しています。Apache Tomcat、IBM Websphere、Oracle WebLogic ServerおよびJBOSSでは、JDBC/UCPのサポートを活用してシャーディングを使用できます。PHP、Python、PerlおよびNode.jsではOCIのサポートを活用できます。

シャード・トポロジ・キャッシュは、シャードとシャーディング・キーの範囲のマッピングです。Oracle統合接続プールは、このシャード・トポロジ・キャッシュをそのメモリー内に保持します。特定のシャードへの最初の接続時(プールの初期化時またはプールが新しいシャードに接続するとき)に、シャーディング・キーの範囲のマッピングがシャードから収集されて、シャード・トポロジ・キャッシュが動的に作成されます。

シャード・トポロジをキャッシュすると、シャードへの高速なパスが作成され、シャードへの接続を作成するプロセスが迅速になります。シャーディング・キーを使用して接続リクエストが実行されると、接続プールはこの特定のシャーディング・キーが存在する対応するシャードを検索します(トポロジ・キャッシュから)。一致する接続がプールで利用できる場合、プールは内部接続選択アルゴリズムを適用することによって、シャードへの接続を返します。

キャッシュされたトポロジ・マップに存在する特定のシャーディング・キーへのデータベース接続リクエストは、そのシャードに直接送られます(つまり、シャード・ディレクタがバイパスされます)。また、接続プールは、SDBからRLB通知をサブスクライブし、実行時ロード・バランシング・アドバイザに基づいて最適な接続を分配します。接続が確立されると、クライアントはシャードに対するトランザクションを直接実行します。指定したシャーディング・キーのすべてのトランザクションが実行されたら、アプリケーションはその接続をプールに返し、別のキーの接続を取得する必要があります。

一致する接続がプールにない場合は、シャーディング・キーとともに接続リクエストをシャード・ディレクタに転送することによって、新しい接続が作成されます。

プールが初期化され、シャード・トポロジ・キャッシュがすべてのシャードに基づいて作成されると、シャード・ディレクタが停止しても直接ルーティングに影響しなくなります。

既存のアプリケーションのシャーディングに対する適合性

シャード・アーキテクチャの利点を享受するには、シャーディングを使用する予定がない既存のアプリケーションで、一定のレベルの再設計が必要となります。

シャーディング・キーを渡すのみで済む簡単な場合や、シャード・データベースで必要となるデータおよびワークロードの水平パーティション化を行うことができない場合があります。

電子商取引、モバイル、ソーシャル・メディアなど、多くの顧客対応Webアプリケーションはシャーディングに適切です。そのようなアプリケーションには、適切に定義されたデータ・モデルおよびデータ分散方法(ハッシュ、範囲、リストまたはコンポジット)があり、主にシャーディング・キーを使用してデータにアクセスします。シャーディング・キーの例として顧客ID、アカウント番号、country_idなどがあります。通常、アプリケーションでは、シャーディングが十分に機能するためにデータの部分的な非正規化も必要となります。

シャーディング・キーの単一値に関連付けられたデータにアクセスするトランザクションは、シャード・データベースの主なユースケースです。たとえば、顧客のレコード、サブスクライバのドキュメント、財務トランザクション、電子商取引トランザクションなどの検索および更新などです。同じ値のシャーディング・キーを持つ、シャード・スキーマ内のすべての行は同じシャード上にあることが保証されるため、そのようなトランザクションは常に単一のシャードで最高のパフォーマンスで実行され、最高レベルの一貫性となります。

マルチシャード操作はサポートされますが、パフォーマンスおよび一貫性のレベルは低くなります。そのようなトランザクションには単純な集計、レポートなどが含まれ、単一シャードのトランザクションが優位を占めるワークロードに比べて、シャード・アプリケーションで重要度の低い役割を果たします。

直接ルーティングをサポートするシャーディングAPI

Oracle接続プールおよびドライバはOracle Shardingをサポートしています。

JDBC、UCP、OCIおよびOracle Data Provider for .NET (ODP.NET)は、接続チェックの一部としてシャーディング・キーを認識します。Apache Tomcat、WebsphereおよびWebLogicはシャーディングのためのUCPサポートを活用し、PHP、Python、PerlおよびNode.jsはOCIサポートを活用します。

Oracle Sharding対応のOracle JDBC API

Oracle Java Database Connectivity (JDBC)には、Oracle Sharding構成内のデータベース・シャードに接続するために使用できるAPIがあります。

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

シャード対応アプリケーションは、データベース・シャーディングAPIを使用してシャーディング・キーを指定することで特定のシャードに接続します。

  • OracleShardingKeyインタフェースは、現在のオブジェクトがOracleのシャード・データベースで使用されるOracleシャーディング・キーを表していることを示します。
  • OracleShardingKeyBuilderインタフェースは、サポートされている様々なデータ型のサブキーを持つ複合シャーディング・キーを作成します。このインタフェースでは、新しいJDK 8ビルダー・パターンを使用してシャーディング・キーを作成します。
  • OracleConnectionBuilderインタフェースは、ユーザー名とパスワード以外の追加パラメータを使用して、接続オブジェクトを構築します。
  • OracleDataSourceクラスにより、createConnectionBuilderメソッドとcreateShardingKeyBuliderメソッドはデータベース・シャードのサポートが可能になります。
  • OracleXADataSourceクラスにより、createConnectionBuilderメソッドはデータベース・シャードのサポートが可能になります。
  • OracleConnectionクラスにより、setShardingKeyIfValidメソッドとsetShardingKeyメソッドはデータベース・シャードのサポートが可能になります。
  • OracleXAConnectionクラスにより、setShardingKeyIfValidメソッドとsetShardingKeyメソッドはデータベース・シャードのサポートが可能になります。

詳細と例は、『Oracle Database JDBC開発者ガイド』を参照してください。

例8-1 JDBCを使用したシャード対応アプリケーション・コードの例

次のコードでは、JDBCシャーディングAPIの使用方法を示します

OracleDataSource ods = new OracleDataSource();
   ods.setURL("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(HOST=myhost)(PORT=1521)(PROTOCOL=tcp))(CONNECT_DATA=(SERVICE_NAME=myorcldbservicename)))");
   ods.setUser("hr");
   ods.setPassword("hr");
 
  // Employee name is the sharding Key in this example.
  // Build the Sharding Key using employee name as shown below.

   OracleShardingKey employeeNameShardKey = ods.createShardingKeyBuilder()
                                               .subkey("Mary", JDBCType.VARCHAR)// First Name
                                               .subkey("Claire", JDBCType.VARCHAR)// Last Name
                                               .build();

   OracleShardingKey locationSuperShardKey = ods.createShardingKeyBuilder() // Building a super sharding key using location as the key
                                                .subkey("US", JDBCType.VARCHAR)
                                                .build();

   OracleConnection connection = ods.createConnectionBuilder()
                                    .shardingKey(employeeNameShardKey)
                                    .superShardingKey(locationSuperShardKey)
                                    .build();

Oracle Sharding対応のOracle Call Interface

Oracle Call Interface (OCI)には、Oracle Sharding構成内のデータベース・シャードに接続するために使用できるインタフェースがあります。

チャンクを対象に読取りまたは書込みを行う要求を作成するには、接続開始ステップでそのチャンクを格納している適切なデータベース(シャード)にアプリケーションをルーティングする必要があります。このルーティングを行うには、データ・キーを使用します。データ・キーにより、特定のチャンクへのルーティング(シャーディング・キーを指定)またはチャンクのグループへのルーティング(スーパー・シャーディング・キーを指定)が可能になります。

操作対象のチャンクを含む適切なシャードに接続するためには、アプリケーションでキーを指定してから、シャードされたOracle Databaseへの接続(スタンドアロン接続またはOCIセッション・プールから取得された接続)を取得する必要があります。OCIセッション・プールでは、プールから接続をチェックアウトする前にデータ・キーを指定する必要があります。

大まかに言うと、シャーディング・キーとシャード・グループ・キーを構成して、基礎となる接続でセッションを取得するには、次のステップを実行する必要があります。

  1. シャーディング・キー記述子を割り当てるため、OCIDescriptorAlloc()をコールし、シャーディング・キーを構成するためにOCI_DTYPE_SHARDING_KEYとして記述子型パラメータを指定します。
  2. シャード・グループ・キー記述子を割り当てるため、OCIDescriptorAlloc()をコールし、シャード・グループ・キーを構成するためにOCI_DTYPE_SHARDING_KEYとして記述子型パラメータを指定します。
  3. シャーディング・キーおよびシャード・グループ・キー情報が含まれる前のステップの初期化済認証ハンドルを使用してOCISessionGet()をコールし、シャーディング・キーで指定されたシャードおよびチャンクと、シャード・グループ・キーで指定されたチャンクのグループに対するデータベース接続を取得します。

OCIセッション・プールへの接続、スタンドアロン接続、およびカスタム・プール接続の作成の詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。

Oracle Sharding対応のOracle Universal Connection Pool API

Oracle Universal Connection Pool (UCP)には、Oracle Sharding構成内のデータベース・シャードに接続するために使用できるAPIがあります。

シャード対応のアプリケーションは、createShardingKeyBuilderおよびcreateConnectionBuilderという拡張シャーディングAPIを使用してシャーディング・キーを指定することによって、特定のシャードへの接続を取得します。

大まかに言うと、アプリケーションがシャード・データベースと連携するようにするには、次のステップに従う必要があります。

  1. シャード・ディレクタおよびグローバル・サービスが反映されるようにURLを更新します。

  2. 次のプール・パラメータをプール・レベルおよびシャード・レベルで設定します。

    • setInitialPoolSizeでは、UCPの開始時に作成する接続の初期数を設定します。

    • setMinPoolSizeでは、実行時にプールで維持する接続の最小数を設定します。

    • setMaxPoolSizeでは、接続プールに許容される接続の最大数を設定します

    • setMaxConnectionsPerShardでは、シャードごとの最大接続数を設定します

  3. createShardingKeyBuilderを使用してシャーディング・キー・オブジェクトを作成します。

  4. createConnectionBuilderを使用して接続を確立します。

  5. 特定のシャードのスコープ内でトランザクションを実行します。

例8-2 UCPシャーディングAPIを使用した接続の確立

シャーディング・キーを作成し、UCPシャーディングAPIコールを使用して接続を確立する方法を示すコード片を次に示します。

...

PoolDataSource pds =                                
     PoolDataSourceFactory.getPoolDataSource();
  
  // Set Connection Pool properties
pds.setURL(DB_URL);
pds.setUser("hr");  
pds.setPassword("****");
pds.setInitialPoolSize(10);
pds.setMinPoolSize(20);
pds.setMaxPoolSize(30);
                
// build the sharding key object

OracleShardingKey shardingKey = 
    pds.createShardingKeyBuilder() 
      .subkey("mary.smith@example.com", OracleType.VARCHAR2)
      .build(); 

  // Get an UCP connection for a shard
Connection conn = 
    pds.createConnectionBuilder()
     .shardingKey(shardingKey)
     .build();
...

例8-3 UCP接続プールを使用したシャード対応アプリケーション・コードの例

この例では、プール設定をプール・レベルおよびシャード・レベルで定義しています。

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import oracle.jdbc.OracleShardingKey;
import oracle.jdbc.OracleType;
import oracle.jdbc.pool.OracleDataSource;
import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceFactory;

public class MaxConnPerShard
{    
  public static void main(String[] args) throws SQLException
  {    
    String url = "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(HOST=shard-dir1)(PORT=3216)
 (PROTOCOL=tcp))(CONNECT_DATA=(SERVICE_NAME=shsvc.shpool.oradbcloud)(REGION=east)))";
    String user="testuser1", pwd = "testuser1";  
  
    int maxPerShard = 100, initPoolSize = 20;    

    PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
    pds.setConnectionFactoryClassName(OracleDataSource.class.getName());
    pds.setURL(url);
    pds.setUser(user);
    pds.setPassword(pwd);
    pds.setConnectionPoolName("testpool");
    pds.setInitialPoolSize(initPoolSize);    

    // set max connection per shard
    pds.setMaxConnectionsPerShard(maxPerShard);
    System.out.println("Max-connections per shard is: "+pds.getMaxConnectionsPerShard());
                
    // build the sharding key object
    int shardingKeyVal = 123;    
    OracleShardingKey sdkey = pds.createShardingKeyBuilder()
        .subkey(shardingKeyVal, OracleType.NUMBER)
        .build();

    // try to build maxPerShard connections with the sharding key
    Connection[] conns = new Connection[maxPerShard];
    for (int i=0; i<maxPerShard; i++)
    {      
      conns[i] = pds.createConnectionBuilder()
          .shardingKey(sdkey)
          .build();
    
Statement stmt = conns[i].createStatement();
      ResultSet rs = stmt.executeQuery("select sys_context('userenv', 'instance_name'),
       sys_context('userenv', 'chunk_id') from dual");
      while (rs.next()) {
        System.out.println((i+1)+" - inst:"+rs.getString(1)+", chunk:"+rs.getString(2));
      }
      rs.close();
      stmt.close();
    }      

    System.out.println("Try to build "+(maxPerShard+1)+" connection ...");
    try {
      Connection conn = pds.createConnectionBuilder()
          .shardingKey(sdkey)
          .build();

      Statement stmt = conn.createStatement();
      ResultSet rs = stmt.executeQuery("select sys_context('userenv', 'instance_name'),
       sys_context('userenv', 'chunk_id') from dual");
      while (rs.next()) {
        System.out.println((maxPerShard+1)+" - inst:"+rs.getString(1)+",
         chunk:"+rs.getString(2));
      }
      rs.close();
      stmt.close();

      System.out.println("Problem!!! could not build connection as max-connections per
        shard exceeded");
      conn.close();
    } catch (SQLException e) {
      System.out.println("Max-connections per shard met, could not build connection
        any more, expected exception: "+e.getMessage());
    }    
    for (int i=0; i<conns.length; i++)
    {
      conns[i].close();
    }        
  }
}

Oracle Sharding対応のOracle Data Provider for .NET API

Oracle Data Provider for .NET (ODP.NET)には、Oracle Sharding構成内のデータベース・シャードに接続するために使用できるAPIがあります。

ODP.NET APIを使用すると、シャード対応アプリケーションはOracleConnectionクラスのSetShardingKey(OracleShardingKey shardingKey, OracleShardingKey superShardingKey)インスタンス・メソッドなどのAPIでシャーディング・キーおよびスーパー・シャーディング・キーを指定することで、特定のシャードへの接続を取得します。

大まかに言うと、.NETアプリケーションがシャード・データベースと連携するようにするには、次のステップを実行する必要があります。

  1. ODP.NET管理対象外ドライバを使用します。

    シャーディングは、ODP.NET接続プールを使用する場合もODP.NET接続プールを使用しない場合もサポートされます。各プールは、シャード・データベースの異なるシャードへの接続を保持できます。

  2. OracleShardingKeyクラスを使用して、シャーディング・キー、およびスーパー・シャーディング・キーの別のインスタンスを設定します。

  3. ODP.NETが指定したシャーディング・キーおよびスーパー・シャーディング・キーを使用する接続を返すことができるように、OracleConnection.Open()を呼び出す前に、OracleConnection.SetShardingKey()メソッドを呼び出します。

    これらのキーは、OracleConnectionがクローズ状態のときに設定する必要があります。そうしないと、例外がスローされます。

例8-4 ODP.NETを使用したシャード対応アプリケーション・コードの例

using System;
using Oracle.DataAccess.Client;
 
class Sharding
{
  static void Main()
  {
    OracleConnection con = new OracleConnection
      ("user id=hr;password=hr;Data Source=orcl;");
    //Setting a shard key
    OracleShardingKey shardingKey = new OracleShardingKey(OracleDbType.Int32, 123);
    //Setting a second shard key value for a composite key
    shardingKey.SetShardingKey(OracleDbType.Varchar2, "gold");
    //Creating and setting the super shard key
    OracleShardingKey superShardingKey = new OracleShardingKey();
    superShardingKey.SetShardingKey(OracleDbType.Int32, 1000);
    
    //Setting super sharding key and sharding key on the connection
    con.SetShardingKey(shardingKey, superShardingKey);
    con.Open();

    //perform SQL query
  }
}