パフォーマンスチューニング
Javaでバッチとか、同一コネクション上で、同じSQL(Preparedな)を大量に発行している場合、且つ、生成したPreparedStatementをSQL発行の度にcloseしているような実装が既に存在しており、ロジックの変更はしたくないといった場合のやっつけパフォーマンス改善手段。
具体的には、PreparedStatementのキャッシュと、closeの遅延をやるんだけども、Connection内でPreparedStatementをSQL文そのものをキーとしてMapにキャッシュし、ステートメントのcloseはConnectionのクローズ時にまとめてやってしまえというもの。(発行しているSQL文の種類が多すぎると、メモリ食いすぎて使えないので注意。><)
java.sql.PreparedStatementのラッパを作る。
public class PreparedStatementCache implements PreparedStatement { private PreparedStatement _pstmt = null; public PreparedStatementForCache(PreparedStatement pstmt) { _pstmt = pstmt; } @Override public void close() throws SQLException { //なにもしない、させない。 } //後はすべて、_pstmtへの委譲メソッド。 }
java.sql.Connectionのラッパを作る。
public class StatementCacheConnection implements Connection { private Connection _con = null; private Map<String,PreparedStatement> _stmtCache = null; public StatementcacheConnection(Connection con) { _con = con; //_stmtCache = new HashMap<String,PreparedStatement>();↓コメント欄での指摘により修正 _stmtCache = new IdentityHashMap<String,PreparedStatement>(); } public void close() throws SQLException { //念のため、キャッシュしたステートメントをすべてclose。 for(PreparedStatement stmt : _stmtCash.values()) { stmt.close(); } _stmtCache.clear();//キャッシュをぶっ飛ばす。 _con.close(); } public PreparedStatement prepareStatement(String sql) throws SQLException { PreparedStatement stmt = _stmtCache.get(sql); if (stmt == null) { stmt = _con.prepareStatement(sql); _stmtCache.put(sql,stmt); } return new PreparedStatementForCache(stmt); //ラップしる } //あとは、全てのprepareStatementを同じように実装 //他は全て_conへの委譲メソッド }
生のConnectionをラップ
JDBCのConnectionを生成もしくは取得している場所で、生コネクションをStatementCashConnectionでラップすればおk。
以上。
この前やっつけ仕事で使ったら超速。
びびった。
俺すげーとか勘違いした。
ラッキー。
まぁ、たまたまボトルネックにクリーンヒットしただけなんだけども...
パフォーマンスチューニングには計測が大事ですよね。ええ。わかってますよ。
Oracleの場合、SQLの種類よりもMAX_CURSORSの値が多くないとエラーとなる。Statementを開きっぱなしにするということはカーソルを開きっぱなしにするということと同じ。