E トラブルシューティング
この付録では、Java Database Connectivity (JDBC)アプリケーションのトラブルシューティングについて説明します。内容は次のとおりです。
E.1 一般的な問題
E.1.1 OUTまたはIN/OUT変数として定義されたCHAR列に対するメモリー消費
PL/SQLでは、CHAR
またはVARCHAR2
列がOUT
またはIN/OUT
変数として定義される場合、ドライバは32512文字のCHAR
配列を割り当てます。このため、メモリー消費に関する問題が生じます。JDBC Thinドライバは、VARCHAR2
出力型を使用する場合、メモリーを割り当てません。ただし、JDBC OCIドライバは、CHAR
とVARCHAR2
の両方の型に対してメモリーを割り当てます。このため、OCIドライバのCPU負荷はThinドライバより高くなります。
以前のリリースでは、この問題はStatement.setMaxFieldSize
メソッドをコールすることで解決できました。これよりもよい解決方法は、OracleCallableStatement.registerOutParameter
を使用することです。すべてのCHAR
またはVARCHAR2
列で、必ずregisterOutParameter(int paramIndex, int sqlType, int scale, int maxLength)
をコールすることをお薦めします。このメソッドは、oracle.jdbc.OracleCallableStatement
に定義されています。4つ目の引数maxLength
を使用して、メモリー消費を制限します。このパラメータは、この列の格納に必要な文字数をドライバに指示します。文字配列に列データを保持できない場合、列は切り捨てられます。3つ目の引数scale
は、ドライバでは無視されます。
E.1.2 メモリー・リークおよびカーソルの不足
カーソルまたはメモリーが不足しているというメッセージを受け取った場合は、すべてのStatement
およびResultSet
オブジェクトを明示的にクローズしてください。Oracle JDBCドライバには、ファイナライザ・メソッドがありません。クリーン・アップ・ルーチンは、ResultSet
およびStatement
クラスのclose
メソッドで実行されます。結果セットおよび文オブジェクトを明示的にクローズしておかないと、重大なメモリー・リークが発生します。また、データベースのカーソルが不足します。文をクローズすると、データベース内の対応するカーソルが解放されます。
同様に、サーバー側でのリークおよびカーソル不足を避けるには、Connection
オブジェクトも明示的にクローズしておく必要があります。接続をクローズすると、その接続に関連付けられたオープン中の文オブジェクトが、JDBCドライバによってクローズされ、サーバー側のカーソルが解放されます。
E.1.3 1プロセスで17以上のOCI接続のオープン数について
必ずしも1プロセスで約17以上のJDBC-OCI接続をオープンできないことがあります。サーバー上のプロセス数が初期化ファイルに指定されている制限を超えたためか、またはプロセスごとのファイル記述子の制限を超えた可能性があります。1つのJDBC-OCI接続で、複数のファイル記述子が使用される(3から4個のファイル記述子が使用されます)ことがあります。
サーバーで17以上のプロセスを使用可能にしている場合は、プロセスごとのファイル記述子の制限が原因である可能性があります。この制限数を増やすと、解決する可能性があります。
E.1.4 Statement.cancelの使用
JDBC標準メソッドStatement.cancel
は、データベースにメッセージを送信することにより、SQL文の実行を正常に停止させます。データベースは、これに応答して実行を停止し、エラー・メッセージを返します。Statement.execute
をコールしたJavaスレッドはサーバーで待機し、他のスレッドからのStatement.cancel
メソッドのコールにより起動された返答のエラー・メッセージを受信した場合にのみ実行を続けます。
このため、Statement.cancel
メソッドは、ネットワークとデータベースが正しく機能していることに依存します。ネットワーク接続が切断するか、データベース・サーバーがダウンした場合、クライアントは取消しメッセージに対するエラー応答を受け取りません。サーバー・プロセスに障害が発生すると、多くの場合、JDBCはStatement.execute
をコールしたスレッドを解放するIOException
を受け取ります。ただし、サーバーがダウンしてもJDBCがIOException
を受け取らない場合があります。Statement.execute
メソッドを開始したスレッドは、Statement.cancel
メソッドによって解放されません。
ノート:
Statement.cancel
メソッドの使用時には次の点に注意してください。
-
接続レベルの取消しと文レベルの取消しが区別されます。文の実行以外の場合、たとえば
ROLLBACK
がstatement.cancel
メソッドによって取り消された場合、コマンドがリプレイされます(ROLLBACK
,COMMIT
、autoCommit ON
、autoCommit OFF
、VERSION
の場合のみ)。データの整合性を保証するために、文の実行をリプレイしません。 -
取消しのコールがデータベースに送信されるまで実行が戻されないように、文の実行と文の取消しを同期します。これによって、文の実行を取り消せるようになります。
-
取消しコールを同期します。これにより、処理中の取消しが処理を完了するまで(データベースが中断、実行およびJDBCへの通知を受け取った後)、新しい取消しリクエストが無視されます。
JDBCがIOException
を受け取らない場合は、Oracle Netがいずれはタイムアウトし、接続をクローズすることになります。これにより、IOException
が発生し、スレッドが解放されます。ただし、このプロセスにはかなりの時間がかかることがあります。このタイムアウトを制御する方法は、OracleDatasource.setConnectionProperties()
のreadTimeout
プロパティの説明を参照してください。このタイムアウトは、Oracle Netの設定を使用してチューニングすることもできます。
JDBC標準メソッドStatement.setQueryTimeout
は、Statement.cancel
メソッドに依存します。指定されたタイムアウト間隔よりも実行時間が長くなった場合、モニター・スレッドがStatement.cancel
メソッドをコールします。これは、前述と同じすべての制限を受けます。この結果、Statement.execute
メソッドをコールしたスレッドがタイムアウトにより解放されない場合があります。
実行と取消しの間の時間の長さは、それほど正確ではありません。この間隔は、指定されたタイムアウト間隔より短くはなりませんが、数秒間長くなることがあります。アプリケーションに、高い優先順位で実行されているアクティブ・スレッドがある場合、この間隔は随意に長くなることがあります。モニター・スレッドは高い優先順位で実行されますが、他にも高い優先順位のスレッドがあり、それが実行し続けられる場合があります。モニター・スレッドが開始されるのは、ゼロ以外のタイムアウトで実行される文がある場合のみです。すべてのOracle JDBC文の実行を監視するモニター・スレッドは、1つのみあります。
ノート:
Statement.cancel
メソッドとStatement.setQueryTimeout
メソッドは、サーバー側内部ドライバでサポートされません。サーバー側内部ドライバは単一スレッドのサーバー・プロセスで動作します。Oracle JVMはこの単一スレッド・プロセス内にJavaスレッドを実装します。サーバー側内部ドライバがSQL文を実行している場合、JavaスレッドはStatement.cancel
メソッドをコールできません。これは、Oracle JDBC監視スレッドにも適用されます。
E.1.5 ファイアウォールとJDBCの使用
アイドル接続に対するファイアウォール・タイムアウトによって、接続が切断される場合があります。このために、JDBCアプリケーションが接続の待機中に停止する可能性があります。ファイアウォール・タイムアウトによって接続が切断されないようにするには、次の処理を1つ以上実行します。
-
接続キャッシュまたは接続プーリングを使用している場合は、接続キャッシュのInactivityタイムアウト値を、常にファイアウォールのアイドル・タイムアウト値より短い値に設定します。
-
接続プロパティとして
oracle.jdbc.ReadTimeout
を渡してソケット上で読込みタイムアウトを有効にします。タイムアウト値はミリ秒で指定します。 -
JDBC OCIドライバとJDBC Thinドライバの両方について、ネット記述子を使用してデータベースに接続し、接続記述子の
DESCRIPTION
句にENABLE=BROKEN
パラメータを指定します。また、TCP_KEEPALIVE_INTERVAL
の下限値も設定します。 -
サーバー側の
sqlnet.ora
ファイルにSQLNET.EXPIRE_TIME=1
を設定して、Oracle Net DCDを使用可能にします。
E.1.6 多数回にわたるサーバーからの突然の切断
ネットワークの信頼性が低い場合、サーバーの接続が突然切断されても、クライアントでは頻繁に発生する切断を検出するのが困難です。デフォルトでは、Linux上で稼働するクライアントは、突然の切断を検出するのに7200秒(2時間)かかります。この値は、tcp_keepalive_time
プロパティの値と同じです。アプリケーションが切断をより迅速に検出するようにするには、tcp_keepalive_time
、tcp_keepalive_interval
およびtcp_keepalive_probes
プロパティの値をオペレーティング・システム・レベルでより小さい値に設定する必要があります。
ノート:
tcp_keepalive_interval
プロパティに小さい値を設定すると、ネットワーク上にプローブ・パケットが頻繁に送出され、システム速度が低下する可能性があります。そのため、このプロパティの値は、システム要件に基づいて適切に設定してください。
また、接続記述子のDESCRIPTION
句にENABLE=BROKEN
パラメータを指定する必要があります。次に例を示します。
jdbc:oracle:thin:@(DESCRIPTION=(ENABLE=BROKEN)(ADDRESS=(PROTOCOL=tcp)(PORT=5221)(HOST=myhost))(CONNECT_DATA=(SERVICE_NAME=orcl)))
E.1.7 ネットワーク・アダプタで接続を確立できない
JDBCアプリケーションからOracleインスタンスへの接続を確立しようとしているときに、次のエラーが発生する場合があります。
java.sql.SQLException: Io exception: The Network Adapter could not establish connection SQLException: SQLState (null) vendor code (17002)
このエラーは、次の状況のいずれかまたは全部に当てはまる場合でも発生することがあります。
-
同じクライアントから同じOracleインスタンスへのSQL*Plus接続を確立できる。
-
JDBC OCI接続は確立できるが、同じクライアントから同じOracleインスタンスへのJDBC Thin接続は確立できない。
-
同じJDBCアプリケーションが別のクライアントから同じOracleインスタンスに接続できる。
-
初期JDBC接続文字列がホスト名を指定していようとIPアドレスを指定していようと、同じ動作が適用される。
このエラーの原因として、次の理由の1つ以上が考えられます。
-
接続を確立しようとしているホスト名が正しくない。
-
接続の確立に使用しているポート番号が間違っている。
-
NICカードでIPv4とIPv6の両方がサポートされている。
-
OracleインスタンスがMTS用に構成されているのに、JDBC接続で専用サーバーではなく共有サーバーを使用している。
SQL*Plusを使用すると、前述の原因を短時間で診断できます。ただし、NICカードに問題がある場合を除きます。次の各項では、このエラーの解決方法とサンプル・アプリケーションを示します。
E.1.7.1 MTSサーバーで構成したOracleインスタンスが共有サーバーを使用
このエラーを解決するには、Oracleインスタンスがマルチスレッド・サーバー(MTS)用に構成されているかどうかを確認する必要があります。OracleインスタンスがMTS用に構成されていない場合は、構成する必要があります。
OracleインスタンスがMTS用に構成されている場合は、JDBC接続が共有サーバーではなく専用サーバーを使用するように強制する必要があります。これを行うには、専用接続のみを使用するようにサーバーを再構成します。専用接続のみを使用するようにサーバーを構成することが実現可能ではない場合は、次のステップを実行してクライアント側から設定します。
JDBC OCIクライアントの場合
-
クライアントの
tnsnames.ora
ファイルに格納されているTNS接続文字列に(SERVER=DEDICATED)
プロパティを追加します。 -
クライアントの
sqlnet.ora
ファイルでUSER_DEDICATED_SERVER=ON
を設定します。
JDBC Thinの場合:
短縮JDBC Thin構文ではなく、フルネーム/値ペアの接続文字列(tnsnames.ora
ファイルに指定されているのと同じ)を指定する必要があります。たとえば、"jdbc:oracle:thin:@host:port:sid
"接続文字列ではなく、次の形式の接続文字列を使用する必要があります。
"jdbc:oracle:thin:@(DESCRIPTION=" + "(ADDRESS_LIST=" + "(ADDRESS=(PROTOCOL=TCP)" + "(HOST=host)" + "(PORT=port)" + ")" + ")" + "(CONNECT_DATA=" + "(SERVICE_NAME=sid)" + "(SERVER=DEDICATED)" + ")" + ")"
E.1.7.2 IPv4とIPv6の両方をサポートするNICカードを保有するJDBC Thinドライバ
サーバーのネットワーク・インタフェース・コントローラ(NIC)カードがIPv4とIPv6の両方をサポートするように構成されている場合、サービスによってはIPv6で起動することがあります。IPv6で実行中のサービスにIPv4を使用して(またはその逆)接続しようとするクライアント・アプリケーションはいずれも接続拒否エラーを受け取ります。JDBC thinクライアント・アプリケーションがデータベース・サーバーに接続しようとすると、そのアプリケーションは応答を停止するか、次のエラーで失敗する可能性があります。
java.sql.SQLException: Io exception: The Network Adapter could not establish the connection Error Code: 17002
次の解決策のいずれかを使用してこのエラーを解消してください。
-
Java仮想マシン(JVM)に、IPプロトコル・バージョン4を使用するように指定します。
-Djava.net.preferIPv4Stack
パラメータをtrue
に設定して、JDBCアプリケーションが実行されているJVMを起動します。たとえば、jdbcTest
という名前のJDBCアプリケーションを実行しているとします。その場合、アプリケーションを次の方法で実行します。java -Djava.net.preferIPv4Stack=true jdbcTest
-
OCI JDBCドライバを使用します。
E.1.7.3 サンプル・アプリケーション
例E-1に、データベースに接続し、接続のテストに使用できる基本的なJDBCプログラムを示します。Oracle JDBCドライバを使用する、あらゆる形式の接続を試すことができます。
例E-1 5つの異なる方法でデータベースに接続する基本的なJDBCプログラム
import java.sql.*; public class Jdbctest { public static void main (String args[]) { try { /* Uncomment the next line for more connection information */ // DriverManager.setLogStream(System.out); /* Set the host, port, and sid below to match the entries in the listener.ora */ String host = "myhost.oracle.com"; String port = "5221"; String sid = "orcl"; // or pass on command line arguments for all three items if ( args.length >= 3 ) { host = args[0]; port = args[1]; sid = args[2]; } String s1 = "jdbc:oracle:thin:@" + host + ":" + port + ":" + sid ; if ( args.length == 1 ) { s1 = "jdbc:oracle:oci8:@" + args[0]; } if ( args.length == 4 ) { s1 = "jdbc:oracle:" + args[3] + ":@" + "(description=(address=(host=" + host+ ")(protocol=tcp)(port=" + port+ "))(connect_data=(sid="+ sid + ")))"; } System.out.println( "Connecting with: " ); System.out.println( s1 ); DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver()); Connection conn = DriverManager.getConnection( s1,"hr","hr"); DatabaseMetaData dmd = conn.getMetaData(); System.out.println("DriverVersion:["+dmd.getDriverVersion()+"]"); System.out.println("DriverMajorVersion: ["+dmd.getDriverMajorVersion()+"]"); System.out.println("DriverMinorVersion: ["+dmd.getDriverMinorVersion()+"]"); System.out.println("DriverName:["+dmd.getDriverName()+"]"); if ( conn!=null ) conn.close(); System.out.println("Done."); } catch ( SQLException e ) { System.out.println ("\n*** Java Stack Trace ***\n"); e.printStackTrace(); System.out.println ("\n*** SQLException caught ***\n"); while ( e != null ) { System.out.println ("SQLState: " + e.getSQLState ()); System.out.println ("Message: " + e.getMessage ()); System.out.println ("Error Code: " + e.getErrorCode ()); e = e.getNextException (); System.out.println (""); } } } }
E.2 基本的なデバッグ処理
関連トピック
E.2.1 ネットワーク・イベントをトラップするためのOracle Netトレース
クライアントおよびサーバーのOracle-Netトレースが、Oracle Net経由で送信されるパケットをトラップできるようにできます。クライアント側トレースはJDBC OCIドライバについてのみ使用できます。JDBC Thinドライバについてはサポートされません。
トレース機能を使用すると、ネットワーク・イベントが実行されるたびにそのイベントについて記述される、一連の詳細な文が生成されます。操作をトレースすることにより、イベントの内部操作に関する詳細な情報を取り出すことができます。この情報は読込み可能ファイルに出力され、エラーの原因となったイベントを特定できます。トレース情報の収集は、SQLNET.ORA
ファイルにあるいくつかのOracle Netパラメータによって制御されます。SQLNET.ORA
のパラメータを設定した後に、トレースを実行するために新しい接続を作成する必要があります。
トレース・レベルを高くすると、より詳細な情報がトレース・ファイルに取得されます。トレース・ファイルの理解が難しくなるため、トレースを有効にする場合はトレース・レベル4から開始します。トレース・ファイルの最初の部分は接続ハンドシェイク情報です。JDBCプログラムに関連するSQL文とエラー・メッセージの情報は、その先を参照してください。
ノート:
トレース機能ではディスク領域が大量に使用されるため、システムのパフォーマンスが大幅に低下する可能性があります。トレースは、必要なときにのみ使用可能にしてください。
E.2.1.1 クライアント側でのトレース
E.2.1.1.1 TRACE_LEVEL_CLIENT
目的:
トレースを一定の指定レベルでon
またはoff
にします。
デフォルト値:
0またはOFF
有効な値
-
0または
OFF
- トレースの出力なし -
4または
USER
- ユーザー・トレース情報 -
10または
ADMIN
- 管理トレース情報 -
16または
SUPPORT
- カスタマ・サポート・トレース情報
例:
TRACE_LEVEL_CLIENT=10
E.2.1.1.2 TRACE_DIRECTORY_CLIENT
目的:
トレース・ファイルの書込み先ディレクトリを指定します。
デフォルト値:
ORACLE_HOME
/network/trace
例:
UNIX: TRACE_DIRECTORY_CLIENT=/oracle/traces
Windows: TRACE_DIRECTORY_CLIENT=C:\ORACLE\TRACES
E.2.1.1.3 TRACE_FILE_CLIENT
目的:
クライアント・トレース・ファイルの名前を指定します。
デフォルト値:
SQLNET.TRC
例:
TRACE_FILE_CLIENT=cli_Connection1.trc
ノート:
TRACE_FILE_CLIENT
ファイルのために選択する名前は、TRACE_FILE_SERVER
ファイルのために選択する名前と異なっている必要があります。
E.2.1.2 サーバー側でのトレース
E.2.1.2.1 TRACE_LEVEL_SERVER
目的:
トレースを一定の指定レベルでon
またはoff
にします。
デフォルト値:
0またはOFF
有効な値
-
0または
OFF
- トレースの出力なし -
4または
USER
- ユーザー・トレース情報 -
10または
ADMIN
- 管理トレース情報 -
16または
SUPPORT
- カスタマ・サポート・トレース情報
例:
TRACE_LEVEL_SERVER=10
E.2.1.2.2 TRACE_DIRECTORY_SERVER
目的:
トレース・ファイルの書込み先ディレクトリを指定します。
デフォルト値:
ORACLE_HOME
/network/trace
例:
TRACE_DIRECTORY_SERVER=/oracle/traces