PR

Oracle JDBCのimplicitStatementCacheSizeとmaxCachedBufferSizeの違いを初心者向けに解説

Oracle JDBCのキャッシュ設定を、データベース・SQL文・内部バッファの再利用で表したアイキャッチ画像 Java

2つともキャッシュ設定なのに、何が違うの?

値を大きくすると、何が増えるの?

implicitStatementCacheSize準備済みのSQL文maxCachedBufferSizeドライバ内部の作業用バッファを再利用するための設定です。名前は似ていますが、再利用する対象が違います。

最初に押さえる2つの違い
  • implicitStatementCacheSize:準備済みのSQL文を何個まで再利用するか
  • maxCachedBufferSize:内部バッファをどの大きさまで再利用用に残すか

まずは、前者がSQL文の準備作業、後者がメモリ上の作業スペースに関する設定だと理解できればOKです。

キャッシュという仕組み自体を先に整理したい場合は、キャッシュとは?も参考にしてください。この記事では、同じ検索SQLを何度も実行するWebアプリケーションを例に、2つの設定がどこで働くのかを順番に説明します。

このページで学べる内容
  • Statementキャッシュが再利用するもの
  • 内部バッファのキャッシュが再利用するもの
  • maxCachedBufferSizeの値を2の累乗または実サイズで読む方法
  • WebLogicで2種類のStatementキャッシュを混同しないための注意点
  • カーソル数・ヒープ・GCを使った確認方法
Oracle JDBCの1つの物理接続の中で、implicitStatementCacheSizeは準備済みSQL文の保持数を、maxCachedBufferSizeは内部バッファの保持サイズを制御することを示した図
implicitStatementCacheSizeはStatementの「個数」、maxCachedBufferSizeは内部バッファの「大きさ」の上限です。どちらも物理接続ごとに、次回使えるものを残します。
スポンサーリンク

最初に2つの違いを整理

設定 再利用するもの 値の意味 主な狙い
implicitStatementCacheSize PreparedStatementCallableStatement 1接続あたりに保持する文の最大数 同じSQLを再び準備する処理を減らす
maxCachedBufferSize Oracle JDBCドライバ内部のbyte/charバッファ 再利用用に保持する最大サイズ(30以下は底2の対数、30超は実サイズ) 大きなバッファを抱え続けることによるメモリ消費を抑える

この表だけでは、まだ2つの違いがピンと来ないかもしれません。そこで、同じ商品検索SQLを2回実行する流れを追いながら、どこで何が再利用されるのかを見てみましょう。

まずはPreparedStatementが何を準備しているか

次のSQLを、商品検索画面から何度も実行するとします。

SELECT product_id, product_name, price
  FROM products
 WHERE category_id = ?

?には検索対象のカテゴリIDが入ります。Java側では、概念的に次のような流れになります。

String sql = """
    SELECT product_id, product_name, price
      FROM products
     WHERE category_id = ?
    """;

try (PreparedStatement ps = connection.prepareStatement(sql)) {
    ps.setInt(1, 10);

    try (ResultSet rs = ps.executeQuery()) {
        while (rs.next()) {
            // 検索結果を1行ずつ処理する
        }
    }
}

prepareStatement()では、Javaオブジェクトを1個作るだけではありません。Oracle JDBCドライバとデータベース側では、SQLの解析結果、列やバインド変数の情報、カーソルなど、SQLを実行するための状態が用意されます。

処理が終わるたびに、これらをすべて捨てて次回また準備すると、同じSQLを繰り返すアプリケーションでは無駄が増えます。そこで使われるのが、Statementキャッシュです。

implicitStatementCacheSizeは「SQL文の準備済みセット」の上限

implicitStatementCacheSizeは、Oracle JDBCドライバの暗黙的Statementキャッシュへ、準備済みの文を1接続あたり何個まで保持するかを指定します。

暗黙的キャッシュを有効にすると、アプリケーションがPreparedStatement.close()を呼んだとき、対象の文は直ちに物理的に破棄されず、再利用できる状態でキャッシュへ戻ります。

  1. 最初のprepareStatement(sql)で文を準備する
  2. SQLを実行する
  3. close()でStatementキャッシュへ戻す
  4. 同じ接続で同じSQLを再び準備すると、キャッシュ済みの文を再利用する

たとえば値を50にすると、物理接続ごとに最大50個の文を保持できます。コネクションプールが20接続なら、単純な全体上限のイメージは「最大50文 × 20接続」です。キャッシュはアプリケーション全体で1個ではなく、接続ごとに持つ点が重要です。

キャッシュが満杯になると、Oracle JDBCでは最近使われていない文が追い出されます。また、SQL文字列が少しでも異なれば、別の文として扱われる場合があります。

大きくすればするほど速くなるわけではない

Statementキャッシュには、データベース側のカーソルなどの資源も関係します。値を大きくしすぎると、接続数との掛け算で保持数が膨らみ、OPEN_CURSORSの上限やメモリ消費へ影響します。

Oracleの現行ドキュメントでは、接続プロパティの既定値は0です。つまり、設定しなければこの接続プロパティによる暗黙的Statementキャッシュは無効です。値は「よく使うSQLの種類数」と「物理接続数」を見ながら決めます。

maxCachedBufferSizeは「SQL結果そのもの」のキャッシュではない

同じ検索SQLでも、結果が10行のときと10万行のときでは、JDBCドライバがデータを受け取るために必要な作業用メモリが変わります。

Oracle JDBCドライバは、内部でbyte配列やchar配列などのデータバッファを使います。一度確保したバッファを再利用できれば、次の実行で毎回新しく確保する回数を減らせます。

しかし、ある1回だけ巨大な検索結果を扱ったために大きなバッファを確保し、そのバッファを接続が生きている間ずっと保持すると、ヒープを圧迫する可能性があります。

maxCachedBufferSizeは、検索結果そのものを保存する設定ではありません。 再利用用としてキャッシュする内部バッファの最大サイズを制限します。

  • 検索結果の行数
  • ResultSetを保存しておく件数
  • Statementキャッシュの個数
  • 1回のSQLで取得できるデータ量の上限

上限より大きなバッファが必要なSQLも実行できます。ただし、その大きなバッファを再利用用として残さないため、次回また必要になれば再確保が発生し、性能とメモリ使用量のトレードオフが生まれます。

maxCachedBufferSizeの値はどう読む?

ここが最も間違えやすいポイントです。 20を「20バイト」や「20MB」と読むのは誤りです。値が30以下なら、基本的に2の累乗として読みます。

Oracle JDBC 21cのAPIリファレンスでは、maxCachedBufferSizeを「ドライバがキャッシュする最大の内部char/byteバッファサイズについて、底を2とする対数」と定義しています。たとえば20なら、220が上限の目安です。

設定値 2の累乗 byteバッファで考えた目安
18 218 256KiB
19 219 512KiB
20 220 1MiB
30 230 1GiB
1048576 30を超えるため実サイズ 1MiB

ただし、公式APIでは30を超える値は、底を2とする対数ではなく実際のバッファサイズとして扱うとされています。byteバッファではbyte数、charバッファではchar数です。char配列が占めるヒープ量はbyte配列と同じとは限らないため、表はヒープ消費量を厳密に示すものではありません。

Oracle JDBC 21cおよび26aiのAPIリファレンスでは、既定値は30です。なお、12未満はバッファキャッシュを事実上無効化します。製品固有のチューニングガイドで別の推奨値が示される場合もあるため、利用中のJDBCドライバの版と対象製品の公式手順を優先してください。

同じSQLを2回実行すると、2つの設定はどこで働く?

先ほどの商品検索を、同じ物理接続で2回実行する場面へ戻ります。

1回目

  1. SQL文を準備し、Statementを作る
  2. 検索結果を受け取るため、内部バッファを確保する
  3. 結果をJavaへ返す
  4. Statementと再利用可能なバッファをキャッシュへ戻す

2回目

  1. implicitStatementCacheSizeの範囲内なら、準備済みStatementを再利用する
  2. maxCachedBufferSizeの範囲内なら、内部バッファも再利用できる
  3. 上限を超えて保持されなかったバッファは、必要に応じて再確保する

つまり、2つは同じSQL実行の中で働きますが、再利用する階層が違います。Statementを再利用できても、巨大なバッファまで保持する必要はありません。反対に、バッファを再利用できても、別のSQL文ならStatementの再利用とは別問題です。

設定例

JDBC接続プロパティとして指定する場合の概念例です。

Properties props = new Properties();
props.setProperty("user", "app_user");
props.setProperty("password", "********");

// 1接続あたり50文まで、暗黙的Statementキャッシュへ保持
props.setProperty("oracle.jdbc.implicitStatementCacheSize", "50");

// 2^20を超える内部バッファは、再利用用として保持しない
props.setProperty("oracle.jdbc.maxCachedBufferSize", "20");

Connection connection =
    DriverManager.getConnection(jdbcUrl, props);

実際の指定場所は、アプリケーションサーバー、コネクションプール、フレームワークによって異なります。

WebLogic Serverでは、2種類のStatementキャッシュを混同しないでください。 OracleのWebLogic 15.1.1ドキュメントでは、データソースの接続プロパティにoracle.jdbc.implicitStatementCacheSizeを設定すると、WebLogic側のStatement Cache Sizeは自動的に0になると説明されています。

現場では何を見て調整するか

値だけを見て「大きいから良い」「小さいから安全」とは判断できません。それぞれ、症状と観測対象を分けます。

implicitStatementCacheSizeを確認する場面

  • 同じSQLを何度も実行しているのに、SQL準備のコストが目立つ
  • Statementキャッシュのヒット率を確認したい
  • ORA-01000: maximum open cursors exceededとの関係を切り分けたい
  • 物理接続数を増やした後に、カーソル数やメモリが増えた

キャッシュ数だけでなく、コネクションプールの物理接続数、SQLの種類数、データベースのOPEN_CURSORSをセットで確認します。

maxCachedBufferSizeを確認する場面

  • 巨大な検索やLOB処理の後、Javaヒープ使用量が戻りにくい
  • JFRやヒープダンプで、Oracle JDBC内部の大きなbyte/char配列が目立つ
  • GC(ガベージコレクション)の回数や停止時間が増えている
  • 値を下げた後に、同じ大規模SQLの処理時間が悪化した

Oracleの古いJDBC READMEでも、まず各Statementのフェッチサイズを適切に設定し、それが難しい場合にmaxCachedBufferSizeでメモリフットプリントを抑える考え方が示されています。したがって、巨大な取得結果が原因なら、設定値だけでなくfetchSizeやSQL自体も調査対象です。

よくある誤解

「2つともSQL結果を速くするキャッシュ」ではない

implicitStatementCacheSizeは、準備済みのSQL文を再利用します。maxCachedBufferSizeは、ドライバ内部の作業用メモリをどこまで再利用用に残すかを決めます。検索結果の内容そのものを保存するResult Cacheとは別物です。

maxCachedBufferSizeを小さくしても、SQLの取得量は減らない

この設定は「大きな結果を取得禁止にする上限」ではありません。大きなバッファを使った後、それをキャッシュへ残すかどうかに関係します。転送量を減らしたいなら、SQLの抽出条件、取得列、ページング、フェッチサイズなどを見直します。

implicitStatementCacheSizeだけでデータベースの解析がゼロになるとは限らない

JDBCドライバ側のStatement再利用と、Oracle Database側の共有SQLやカーソル管理は関連しますが同一ではありません。性能問題を調べるときは、Java側の設定だけでなく、AWRやSQL統計でparse回数、実行回数、待機時間も確認します。

まとめ

  • implicitStatementCacheSizeは、1接続あたりに再利用する準備済みStatementの最大数
  • maxCachedBufferSizeは、再利用用として残すOracle JDBC内部バッファの最大サイズ
  • 前者は「SQL文の準備」、後者は「メモリ上の作業スペース」の設定
  • maxCachedBufferSizeの値が30以下なら2の累乗、30を超える値なら実サイズとして読む
  • 調整時は、接続数・SQL種類数・カーソル数・ヒープ・GC・フェッチサイズを合わせて確認する

JavaやJDBCの前提から確認したい場合は、Java学習ロードマップもあわせてご覧ください。

参考:Oracle公式ドキュメント

タイトルとURLをコピーしました