アダプティブ バッファリングの使用
アダプティブ バッファリングは、サーバー カーソルのオーバーヘッドを発生させることなく、あらゆる種類の大きな値のデータを取得できるように設計されています。 アプリケーションでは、ドライバーによってサポートされるすべてのバージョンの SQL Server でアダプティブ バッファリング機能を使用できます。
通常、Microsoft SQL Server JDBC Driver がクエリを実行するときは、すべての結果をサーバーからアプリケーション メモリに取り出します。 この方法では、SQL Server でのリソースの消費が最小化されますが、非常に大きな結果を生成するクエリの場合は、JDBC アプリケーションで OutOfMemoryError がスローされることがあります。
アプリケーションで非常に大きな結果を処理できるようにするために、Microsoft SQL Server JDBC Driver はアダプティブ バッファリングを提供しています。 ドライバーでアダプティブ バッファリングを使用すると、ステートメントの実行結果を SQL Server から取得する処理が、すべて一度にではなく、アプリケーションの必要に応じて行われます。 また、アプリケーションからアクセスできなくなった結果は、ドライバーによって直ちに破棄されます。 アダプティブ バッファリングは次のような場合に効果的です。
- クエリが非常に大きな結果セットを生成する : アプリケーションでは、メモリに格納できる行よりも多くの行を生成する SELECT ステートメントを実行できます。 以前のリリースでは、OutOfMemoryError を避けるために、サーバー カーソルを使用する必要がありました。 アダプティブ バッファリングは、サーバー カーソルを使用することなく、任意の大きな結果セットを順方向専用かつ読み取り専用で渡せるようにします。
- クエリが非常に大きな SQLServerResultSet 列または SQLServerCallableStatement OUT パラメーター値を生成する : アプリケーションでは、アプリケーション メモリに完全に含めるには大きすぎる単一の値 (列または OUT パラメーター) を取得できます。 アダプティブ バッファリングを使用すると、クライアント アプリケーションで getAsciiStream メソッド、getBinaryStream, メソッド、または getCharacterStream メソッドを使用して、そのような値をストリームとして取得できます。 アプリケーションは、ストリームから読み取るときと同じようにして、SQL Server から値を取得します。
注意
アダプティブ バッファリングでは、必要な量のデータだけが、JDBC ドライバーによってバッファリングされます。 ドライバーにバッファーのサイズを制御したり制限したりするためのパブリック メソッドは備わっていません。
アダプティブ バッファリングの設定
JDBC ドライバー Version 2.0 以降では、ドライバーの既定の動作は "adaptive" です。 つまり、アプリケーションから明示的に要求しなくても、アダプティブ バッファリングの動作が適用されます。 ただし、バージョン 1.2 リリースでは、"full" が既定のバッファリング モードであるため、アプリケーションから明示的にアダプティブ バッファリング モードを要求する必要があります。
アプリケーションがステートメントの実行でアダプティブ バッファリングを使用するように要求できる方法は 3 つあります。
- アプリケーションで、接続プロパティ responseBuffering を "adaptive" に設定できます。 接続プロパティの設定の詳細については、「接続プロパティの設定」を参照してください。
- アプリケーションで、SQLServerDataSource オブジェクトの setResponseBuffering メソッドを使用して、その SQLServerDataSource オブジェクトを通じて作成されたすべての接続の応答バッファリング モードを設定できます。
- アプリケーションで、SQLServerStatement クラスの setResponseBuffering メソッドを使用して、特定のステートメント オブジェクトの応答バッファリング モードを設定できます。
JDBC Driver Version 1.2 を使用している場合、setResponseBuffering メソッドを使用するには、Statement オブジェクトを SQLServerStatement クラスにキャストする必要があります。 「大きなデータを読み取るサンプル」および「ストアド プロシージャで大きなデータを読み取るサンプル」のコード例に示されているのは、この古い使用法です。
ただし、JDBC ドライバー Version 2.0 では、実装されているクラス階層を意識することなく、isWrapperFor メソッドおよび unwrap メソッドを使用して、ベンダー固有の機能にアクセスできます。 コード例については、「大きなデータを更新するサンプル」を参照してください。
アダプティブ バッファリングによる大きなデータの取得
get<Type>Stream メソッドを使用して大きな値を 1 回読み取り、SQL Server によって返された順序で ResultSet 列および CallableStatement OUT パラメーターにアクセスする場合、アダプティブ バッファリングにより、結果を処理するときのアプリケーション メモリの使用量を最小限に抑えることができます。 アダプティブ バッファリングを使用すると、次のようになります。
- SQLServerResultSet クラスおよび SQLServerCallableStatement クラスで定義されている get<Type>Stream メソッドは、既定では 1 回だけ読み取るストリームを返します。ただし、アプリケーションによってマークされていれば、このストリームをリセットできます。 アプリケーションでストリームの
reset
を行う必要がある場合、まずそのストリームに対してmark
メソッドを呼び出す必要があります。 - SQLServerClob クラスおよび SQLServerBlob クラスで定義されている get<Type>Stream メソッドは、
mark
メソッドを呼び出すことなく、常にストリームの開始位置に位置を変更できるストリームを返します。
アプリケーションでアダプティブ バッファリングを使用する場合、get<Type>Stream メソッドによって取得される値は、1 回だけ取得できます。 同じオブジェクトの get<Type>Stream メソッドを呼び出した後に、同じ列またはパラメーターで任意の get<Type> メソッドを呼び出すと、"このデータはアクセスされており、この列またはパラメーターに対しては使用できません" という意味のメッセージで例外がスローされます。
アダプティブ バッファリングを使用するうえでのガイドライン
アプリケーションによるメモリ使用量を最小限に抑えるには、開発者は次の重要なガイドラインに従う必要があります。
- アプリケーションが非常に大きな結果セットを処理できるようにするために、接続文字列プロパティ selectMethod=cursor を使用しないようにします。 アダプティブ バッファリング機能を使用すると、アプリケーションがサーバー カーソルを使用することなく、非常に大きな順方向専用の読み取り専用結果セットを処理することが可能になります。 ただし、selectMethod=cursor を設定した場合、その接続で生成された順方向専用、読み取り専用のすべての結果セットに影響が及ぶ点に注意してください。 つまり、わずかな行数の短い結果セットを定期的に処理するようなアプリケーションでは、結果セットごとにサーバー カーソルを作成し、読み取って、閉じる操作を繰り返すことになって、クライアント側とサーバー側の両方でリソース消費が (selectMethod を cursor に設定しなかった場合と比較して) 増えることになります。
- 大きなテキスト値またはバイナリ値は、getBlob メソッドまたは getClob メソッドではなく、getAsciiStream メソッド、getBinaryStream メソッド、または getCharacterStream メソッドを使用して、ストリームとして読み取ります。 Version 1.2 リリース以降の SQLServerCallableStatement クラスには、この目的のための新しい get<Type>Stream メソッドがあります。
- 大きな値を含む可能性がある列は、SELECT ステートメント内で列リストの最後に配置し、SQLServerResultSet の get<Type>Stream メソッドを使用して、選択された順序で列にアクセスします。
- 大きな値を含む可能性がある OUT パラメーターは、SQLServerCallableStatement の作成に使用する SQL のパラメーターの一覧の最後で宣言します。 また、SQLServerCallableStatement の get<Type>Stream メソッドを使用して、宣言された順序で OUT パラメーターにアクセスします。
- 同じ接続で複数のステートメントを同時に実行しないでください。 前のステートメントの結果を処理する前に別のステートメントを実行すると、処理されない結果がアプリケーションのメモリにバッファリングされることがあります。
- 場合によっては、responseBuffering=adaptive ではなく selectMethod=cursor を使用した方がよいケースもあります。たとえば、次のようなケースです。
- ユーザー入力の後で 1 つずつ行を読み取るなど、順方向専用、読み取り専用の結果セットを少しずつ処理するようなアプリケーションでは、responseBuffering=adaptive よりも selectMethod=cursor を使用した方が、SQL Server によるリソース使用量が少なくて済みます。
- 同じ接続で順方向専用、読み取り専用の複数の結果セットを同時に処理するようなアプリケーションでは、responseBuffering=adaptive の代わりに selectMethod=cursor を使用することで、ドライバーがこれらの結果セットを処理する際に必要となるメモリ量を減らすことができます。
どちらのケースも、サーバー カーソルを作成し、読み取って、閉じるという処理のオーバーヘッドを考慮する必要があります。
スクロール可能な結果セットと順方向専用の更新可能な結果セットの推奨事項を以下に補足します。
- スクロール可能な結果セット : 行をブロック単位でフェッチする場合、常に SQLServerResultSet オブジェクトの getFetchSize メソッドで指定された行数がメモリに読み込まれます。これは、アダプティブ バッファリングが有効になっていたとしても変わりません。 スクロールによって OutOfMemoryError が発生した場合は、フェッチされる行数を減らすことによって対処できます。フェッチ サイズの行数を減らすには、SQLServerResultSet オブジェクトの setFetchSize メソッドを呼び出します。必要であれば、行数を 1 に設定することも検討してください。 それでも OutOfMemoryError を回避できない場合は、あまり大きな列がスクロール可能な結果セットに含まれないようにします。
- 順方向専用の更新可能な結果セット : 行をブロック単位でフェッチする場合、その接続でアダプティブ バッファリングが有効になっていたとしても、通常は SQLServerResultSet オブジェクトの getFetchSize メソッドで指定された行数がメモリに読み込まれます。 SQLServerResultSet オブジェクトの next メソッドを呼び出すと OutOfMemoryError が発生する場合は、フェッチされる行数を減らすことによって対処できます。フェッチ サイズの行数を減らすには、SQLServerResultSet オブジェクトの setFetchSize メソッドを呼び出します。必要であれば、行数を 1 に設定することも検討してください。 SQLServerStatement オブジェクトの setResponseBuffering メソッドを "adaptive" パラメーターで呼び出した後、ステートメントを実行することによって、行のバッファリングを強制的に抑制することもできます。 結果セットはスクロールできないため、アプリケーションから、いずれかの get<Type>Stream メソッドで大きな列の値にアクセスした場合、アプリケーションが読み取りを始めると直ちに、ドライバーが、順方向専用、読み取り専用の結果セットの場合とまったく同じように、その値を破棄します。