2016-06-13 4 views
2

と私は拡張テーブルアンチパターンを実装する私のOracle 11gデータベースに4列のテーブルを持っています。私はいくつかのクエリが非常に長くかかることに気付き、より良いインデックスを作成しようと努力しました。インタラクティブセッションではうまくいっていましたが、SpringのNamedJdbcTemplateを使ってもまだ遅いです。、さらに遅いJdbcTemplate

には、以下のルーチンを考えてみましょう。

private void getObjectIds(ObjectDomain domain, HashMap<String, List<String>> dimensionMap) 
    throws SQLException { 
String sql = "SELECT m2.OBJECT_ID" 
    + " FROM MetaInformations m1, MetaInformations m2\n" 
    + " WHERE m1.OBJECT_ID = m2.OBJECT_ID\n" 
    + " AND m1.OBJECT_DOMAIN = :domain AND m1.KEY = :key1 AND\n" 
    + "  m1.REF_OBJ_VALUE IN (:values1)\n" 
    + " AND m2.OBJECT_DOMAIN = :domain AND m2.KEY = :key2 AND\n" 
    + "  m2.REF_OBJ_VALUE IN (:values2)"; 
String sqlWithBind = "SELECT m2.OBJECT_ID\n" 
    + " FROM MetaInformations m1, MetaInformations m2\n" 
    + " WHERE m1.OBJECT_ID = m2.OBJECT_ID\n" 
    + " AND m1.OBJECT_DOMAIN = ? AND m1.KEY = ? AND\n" 
    + "  m1.REF_OBJ_VALUE IN (?, ?, ?, ?)\n" 
    + " AND m2.OBJECT_DOMAIN = ? AND m2.KEY = ? AND\n" 
    + "  m2.REF_OBJ_VALUE IN (?)"; 

// Prebuilding statement, no bind variables left 
Stopwatch stopWatch2 = Stopwatch.createStarted(); 
Iterator<Entry<String, List<String>>> entries = dimensionMap.entrySet().iterator(); 
Entry<String, List<String>> entry1 = entries.next(); 
Entry<String, List<String>> entry2 = entries.next(); 
String prebuilt = sql.replace(":domain", "'" + domain + "'") 
    .replace(":key1", "'" + entry1.getKey() + "'") 
    .replace(":values1", 
     entry1.getValue().stream().map(s -> "'" + s + "'").collect(Collectors.joining(", "))) 
    .replace(":key2", "'" + entry2.getKey() + "'") 
    .replace(":values2", 
     entry2.getValue().stream().map(s -> "'" + s + "'").collect(Collectors.joining(", "))); 
Set<Long> rs2 = extractIdSet(getNamedParameterJdbcTemplate().queryForRowSet(prebuilt, Collections.emptyMap())); 
log.warn("Prebuilt took: {} ms", stopWatch2.elapsed(TimeUnit.MILLISECONDS)); 

// Simple JDBCTemplate with 9 bind parameters 
Stopwatch stopWatch5 = Stopwatch.createStarted(); 
Set<Long> rs1 = extractIdSet(getJdbcTemplate().queryForRowSet(sqlWithBind, 
    domain.toString(), 
    entry1.getKey(), 
    entry1.getValue().get(0), 
    entry1.getValue().get(1), 
    entry1.getValue().get(2), 
    entry1.getValue().get(3), 
    domain.toString(), 
    entry2.getKey(), 
    entry2.getValue().get(0))); 
log.warn("JdbcTemplate took: {} ms", stopWatch5.elapsed(TimeUnit.MILLISECONDS)); 

// Most beautiful: NamedJDBCTemplate 
Stopwatch stopWatch3 = Stopwatch.createStarted(); 
Map<String, Object> paramMap = createNamedParameterMap(domain, dimensionMap); 
Set<Long> rs3 = extractIdSet(getNamedParameterJdbcTemplate().queryForRowSet(sql, paramMap)); 
log.warn("NamedParameterJdbcTemplate took: {} ms", stopWatch3.elapsed(TimeUnit.MILLISECONDS)); 
} 

は結果があります。正確なタイミングは実行ごとに異なりますが、常に同じオーダーの大きさにとどまっていました。任意のバインド・パラメータなしでクエリを使用して

  1. 100ms未満のために、非常に迅速に完了します。
  2. 9つのバインド変数を持つSpringのJdbcTemplateを使用すると、パフォーマンスはクロールに劣化し、約4秒となります。
  3. 最後に、最も簡単で柔軟性のあるNamedJdbcTemplate、使用、ケース2と同じくらい遅いです。これは、少なくとも、カーテンの後ろ以来NamedJdbcTemplateは、ケース2

と同等の何かそれらはすべて同じ接続プールからそれらを得るようそれは、接続を取得していないに名前付きパラメータと私のクエリを置き換えますは驚くに当たりません。実際には最も速いケースでも使用されるので、queryForRowSet()関数だけではないようです。同じように、Spring 1の例外翻訳や進行中のトランザクションへの参加とは何の関係もないように見えます。これは、ケース1にも影響するはずです。

だから、最終的には、質問:なぜ春のJdbcTemplateバインド・パラメータとは、バインド・パラメータのないプレーンな声明に比べて、この場合のように非常に遅いのですか?

+1

あなたのgistリンクは404になります。コードは** itselff **の質問に投稿してください。 –

+0

コードは_long_です。あなたは本当に質問にそれをしたいですか?とにかく、私は壊れたリンクを修正しました、残念です。 –

+0

はい、それは質問の中にあるべきです。それがここのルールです。私たちはあなたの質問とその答えが、あなたの要点がもう存在しないか、または変更されている場合でも、2年間で理解できるようにします。 –

答えて

0

それはJdbcTemplateNamedJdbcTemplateでもないことが判明します。後者が最も速かったとしても、StatementについてはPreparedStatementではありません。これは、通常のステートメントにバインドパラメータが付いていないためです。バインドパラメータを使用しないクエリがある場合は、raw JDBCの場合と同じ速度で、NamedJdbcTemplateと同じです。

Oracle 11 gは、実際のパラメータに関係なく、9個のバインドパラメータを使用してこのクエリの実行計画を選択するだけです。なぜ私は本当のDBAが利用できないのか分かりません。

同じデータを持つのPostgreSQL 9.3データベースに対する試験は、それがバインドパラメータで及びなしの両方等しく速いことを示しました。すぐに使えるUbuntuのインストールが可能です。