>

2010年9月28日火曜日

JavaからJDBCを使用してOracleにSQLを投げるときのTimezoneの注意点

有名な話ではあるとは思うがJavaのOracle JDBCドライバはタイムゾーン情報を持てない。
※JDBC全般の話なのかOracle特有の話なのかは未確認だがAPI仕様を見る限りJDBC全般の話かも。

JavaからJDBCを使用してOracleにSQLを投げるときにSQLのWHERE句に日時が入っているときはタイムゾーンに関して注意が必要。

PreparedStatementにsetTimeStampやsetDateしてSQLを実行する際、JDBCは引数で渡すDateのエポック秒でSQLを実行するのではなく、引数で渡したDataをマシンのデフォルトタイムゾーン
で出力した(タイムゾーンを伴わない)日時でSQLを実行する。DBを受け取った日時をDBのタイムゾーン設定に基づいて解釈する。

なので特に対策をしないで実行すると次のような問題が起こる。
  • クライアントマシンのタイムゾーンとDB(Oracle)のタイムゾーンが異なると意図する結果が戻らない。
  • クライアントマシンのタイムゾーンが一致していたとしてもJavaで勝手にサマータイムとかが考慮されて一時間ずれた結果が戻ったりする。

特に二つ目の問題は、プログラムをリリースした時期が冬で正常に動作していたのにサマータイムの季節に突然発症したりして厄介。

以上を踏まえるとクライアントのJavaプログラムでSQLを呼び出すときには次の処理を忘れずに行う必要がある。
  1. Javaプログラム中でDBのタイムゾーンをGMT-HH形式で保持する。
    Timezone oracleTZ = TimeZone.getTimeZone("GMT-5");
  2. そのタイムゾーンをデフォルトタイムゾーンとして指定する。
    TimeZone.setDefault(oracleTZ);

デフォルトにすると不都合な場合はPreparedStatementで時刻を扱う都度設定。
PreparedStatement ps = ..... ;
ps.setTimestamp( 1, t, Calendar.getInstance(oracleTZ) );


まとめると
  • OracleJDBC(OracleのみならずJDBC全般の可能性あり)はタイムゾーン情報を持たないためJavaプログラム中にデータベースのタイムゾーン設定する必要がある。
  • Javaプログラムでタイムゾーンを設定する場合は"EST"みたいな三文字略称を使うと勝手にサマータイムとかが考慮されることがあるので"GMT-5"のような形式で設定するのがよい。

捕捉
もっとも大抵の場合はWHERE句でバインド変数の日時を使うのを避けてOracleのSYSDATEなどDB内で完結した表現で日時を設定するのが無難と言えば無難。

2010年9月14日火曜日

CloudFrontによる一回目ダウンロードの速度調査

CloudFrontは基本的にはキャッシュサーバなので一回目のDLでは速度向上は期待できないのではないかという疑問があったので実験してみた。

実験は1.5MBのデータファイルをAmazonS3のBucketから直接DL(RESTのGET API )するのに要する時間と同じファイルをCloudFrontを使用してDLするのに要する時間を比較した。
実験では各イテレーションで実験用ファイルをその都度新規ULしてから時間を計測しているのでCloudFrontからのDLは常に一回目である。実験結果を以下に示す。

【実験条件】
実験日時:2010年4月26日
AmazonS3のBucket置き場所:US
アクセス元:日本
ファイルサイズ;1,722,874バイト
イテレーション数:100回

【結果】
Bucket直接アクセスの平均DL時間:11.7秒
CloudFront経由アクセスの平均DL時間:4.3秒


結果を見てのとおり、CloudFrontはたとえそのコンテンツへの初めてのアクセスであってもDLに要する時間を半分以下に短縮していることが分かった。単なるキャッシュサーバ以上の役割を果たしているようである。