データ依存ルーティングを使用して、クエリを適切なデータベースにルーティングする

適用対象:Azure SQL Database

データ依存ルーティングは、クエリ内のデータを使用して、要求を適切なデータベースにルーティングできる機能です。 データ依存ルーティングは、シャード化されたデータベースを操作するときの基本的なパターンです。 特にシャーディング キーがクエリの一部でない場合は、要求コンテキストを使用して要求をルーティングすることもできます。 データ依存ルーティングを使用したアプリケーションで、特定のクエリまたはトランザクションがそれぞれアクセスするデータベースは、要求ごとに 1 つに制限されています。 Azure SQL Database エラスティック ツールでは、このルーティングは ShardMapManager (Java.NET) クラスで実行します。

アプリケーションは、シャード化された環境の各種データ スライスに関連付けられた、さまざまな接続文字列または DB の場所を追跡する必要はありません。 代わりに Shard Map Manager が正しいデータベースへの接続を、シャード マップ内のデータと、アプリケーション要求の対象であるシャーディング キーの値に基づいて、必要に応じて開きます。 通常、このキーは、データベース要求の基本的なパラメーターである、customer_idtenant_iddate_key、または他の特定の識別子です。

詳細については、「データ依存型ルーティングを使用した SQL Server のスケールアウト」を参照してください。

クライアント ライブラリのダウンロード

ダウンロードの対象:

  • Java バージョンのライブラリは、Maven Central Repository をご覧ください。
  • .NET バージョンのライブラリは、NuGet をご覧ください。

データ依存ルーティング アプリケーションでの ShardMapManager の使用

アプリケーションは、ファクトリ呼び出し GetSQLShardMapManager (Java.NET) を使用して、初期化中に ShardMapManager をインスタンス化する必要があります。 この例では、ShardMapManager と、これが含んでいる特定の ShardMap の両方が初期化されます。 また、GetSqlShardMapManager メソッドと GetRangeShardMap (Java.NET) メソッドを使用しています。

ShardMapManager smm = ShardMapManagerFactory.getSqlShardMapManager(connectionString, ShardMapManagerLoadPolicy.Lazy);
RangeShardMap<int> rangeShardMap = smm.getRangeShardMap(Configuration.getRangeShardMapName(), ShardKeyType.Int32);
ShardMapManager smm = ShardMapManagerFactory.GetSqlShardMapManager(smmConnectionString, ShardMapManagerLoadPolicy.Lazy);
RangeShardMap<int> customerShardMap = smm.GetRangeShardMap<int>("customerMap"); 

シャード マップの取得には最小限のアクセス許可を持つ資格情報を使用する

アプリケーションでシャード マップ自体を操作しない場合は、ファクトリ メソッドで使用される資格情報には、グローバル シャード マップ データベースに対する読み取り専用アクセス許可を付与します。 これらの資格情報は、通常、シャード マップ マネージャーへの接続で使用される資格情報とは異なります。 「 Elastic Database クライアント ライブラリへのアクセスに使用する資格情報」も参照してください。

OpenConnectionForKey メソッドを呼び出す

ShardMap.OpenConnectionForKey メソッド (Java.NET) は、key パラメーターの値に基づいて、適切なデータベースへのコマンド発行の準備が整った接続を返します。 シャード情報は、ShardMapManager によってアプリケーションにキャッシュされるため、これらの要求では通常は、グローバル シャード マップ データベースに対するルックアップは行われません。

// Syntax:
public Connection openConnectionForKey(Object key, String connectionString, ConnectionOptions options)
// Syntax:
public SqlConnection OpenConnectionForKey<TKey>(TKey key, string connectionString, ConnectionOptions options)
  • key パラメーターは、要求に対する適切なデータベースを特定するためのシャード マップへのルックアップ キーとして使用されます。
  • connectionString は、必要な接続を行うためのユーザーの資格情報のみを渡すために使用されます。 この connectionString には、データベース名またはサーバー名は含まれません。メソッドがデータベースとサーバーを特定するために ShardMap を使用するためです。
  • 分割やマージの操作の結果、シャード マップが変更されている可能性や、行が他のデータベースに移動している可能性のある環境では、connectionOptions (Java.NET) を ConnectionOptions.Validate に設定する必要があります。 この検証では、接続要求がアプリケーションに配信される前に、ターゲット データベースにあるローカルのシャード マップ (グローバル シャード マップではない) に対する簡単なクエリが実行されます。

ローカルのシャード マップの検証が失敗した (キャッシュが正しくないことが示された) 場合は、シャード マップ マネージャーがグローバル シャード マップに対してクエリを実行し、ルックアップの正しい値を新しく取得します。その後、キャッシュを更新してから適切なデータベース接続を取得して戻します。

ConnectionOptions.None を使用するのは、アプリケーションがオンラインの間にシャード マッピングの変更が想定されない場合のみです。 その場合、キャッシュされた値は常に正しいと見なすことができ、ターゲット データベースに対する今後のラウンドトリップ検証呼び出しを安全にスキップすることができます。 これによりデータベース トラフィックを軽減できます。 また、 connectionOptions は、一定期間内にシャーディングの変更が想定されるかどうかを示すために構成ファイルの値で設定することもできます。

この例では、customerShardMap という名前のオブジェクト ShardMap を使用した、整数キー CustomerID の値を使用しています。

int customerId = 12345;
int productId = 4321;
// Looks up the key in the shard map and opens a connection to the shard
try (Connection conn = shardMap.openConnectionForKey(customerId, Configuration.getCredentialsConnectionString())) {
    // Create a simple command that will insert or update the customer information
    PreparedStatement ps = conn.prepareStatement("UPDATE Sales.Customer SET PersonID = ? WHERE CustomerID = ?");

    ps.setInt(1, productId);
    ps.setInt(2, customerId);
    ps.executeUpdate();
} catch (SQLException e) {
    e.printStackTrace();
}
int customerId = 12345;
int newPersonId = 4321;

// Connect to the shard for that customer ID. No need to call a SqlConnection
// constructor followed by the Open method.
using (SqlConnection conn = customerShardMap.OpenConnectionForKey(customerId, Configuration.GetCredentialsConnectionString(), ConnectionOptions.Validate))
{
    // Execute a simple command.
    SqlCommand cmd = conn.CreateCommand();
    cmd.CommandText = @"UPDATE Sales.Customer
                        SET PersonID = @newPersonID WHERE CustomerID = @customerID";

    cmd.Parameters.AddWithValue("@customerID", customerId);cmd.Parameters.AddWithValue("@newPersonID", newPersonId);
    cmd.ExecuteNonQuery();
}  

OpenConnectionForKey メソッドは、正しいデータベースに対し、既に開いている接続を新たに返します。 この方法で接続した場合も引き続き接続プーリングの利点を最大限に活用できます。

アプリケーションが非同期プログラミングを使用する場合は、OpenConnectionForKeyAsync method (Java.NET) も使用できます。

一時的な障害処理との統合

クラウドでのデータ アクセス アプリケーション開発のベスト プラクティスは、一時的な障害をアプリケーションで確実に捕捉し、エラーがスローされる前に操作を確実にリトライすることです。 クラウド アプリケーションにおける一時的な障害処理の詳細については、一時的な障害処理 (Java.NET) に関するページをご覧ください。

一時的な障害処理は、データ依存ルーティングのパターンと自然に共存できます。 そのための主な要件は、データ依存ルーティング接続を取得した using ブロックを含めたデータ アクセス要求全体をリトライすることです。 前の例は次のように書き換えることができます。

例 - 一時的な障害処理機能を伴うデータ依存ルーティング

int customerId = 12345;
int productId = 4321;
try {
    SqlDatabaseUtils.getSqlRetryPolicy().executeAction(() -> {
        // Looks up the key in the shard map and opens a connection to the shard
        try (Connection conn = shardMap.openConnectionForKey(customerId, Configuration.getCredentialsConnectionString())) {
            // Create a simple command that will insert or update the customer information
            PreparedStatement ps = conn.prepareStatement("UPDATE Sales.Customer SET PersonID = ? WHERE CustomerID = ?");

            ps.setInt(1, productId);
            ps.setInt(2, customerId);
            ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    });
} catch (Exception e) {
    throw new StoreException(e.getMessage(), e);
}
int customerId = 12345;
int newPersonId = 4321;

Configuration.SqlRetryPolicy.ExecuteAction(() -> {

    // Connect to the shard for a customer ID.
    using (SqlConnection conn = customerShardMap.OpenConnectionForKey(customerId, Configuration.GetCredentialsConnectionString(), ConnectionOptions.Validate))
    {
        // Execute a simple command
        SqlCommand cmd = conn.CreateCommand();

        cmd.CommandText = @"UPDATE Sales.Customer
                            SET PersonID = @newPersonID
                            WHERE CustomerID = @customerID";

        cmd.Parameters.AddWithValue("@customerID", customerId);
        cmd.Parameters.AddWithValue("@newPersonID", newPersonId);
        cmd.ExecuteNonQuery();

        Console.WriteLine("Update completed");
    }
});

一時的な障害処理の実装に必要なパッケージは、エラスティック データベースのサンプル アプリケーションのビルド時に自動的にダウンロードされます。

トランザクションの整合性

トランザクションのプロパティは、シャードにとってローカルなすべての操作で保証されています。 たとえば、データ依存ルーティングを通じて送信されるトランザクションは、接続するターゲット シャードの範囲内で実行されます。 現時点では、複数の接続を 1 つのトランザクションに登録するために提供された機能はありません。したがって、複数のシャードにまたがる操作にもトランザクション上の保証はありません。

次のステップ

シャードをデタッチまたは再アタッチする方法については、「RecoveryManager クラスを使用したシャード マップに関する問題の解決」をご覧ください。

その他のリソース

まだ弾力性データベース ツールを使用していない場合は、 ファースト ステップ ガイドを参照してください。 ご質問がある場合は、SQL Database に関する Microsoft Q&A 質問ページを参照してください。機能に関するご要望は、SQL Database に関するフィードバック フォーラムで新しいアイデアを追加したり、既存のアイデアに投票したりしてください。