2016-06-13 61 views
2

我有四列的表在實施擴展表反模式我的Oracle 11g數據庫。我注意到一些查詢耗時很長,並努力創建更好的索引;在交互式會話中很好,但使用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。

所以最後的問題是:爲什麼Spring的JdbcTemplate使用綁定參數,從而在這種情況下很慢相對於沒有綁定參數普通聲明?

+1

你要點的鏈接導致404後的代碼**的問題itselff **。 –

+0

代碼是_long_。你真的想要這個問題嗎?無論如何,我修復了斷開的鏈接,對此抱歉。 –

+0

是的,它應該在問題中。這是這裏的規則。我們希望您的問題及其答案在兩年內仍然可以理解,當您的要求不存在或已被修改時。 –

回答

0

事實證明,它既不JdbcTemplate也不NamedJdbcTemplate。它也不是約PreparedStatementStatement,即使後者是最快的。那只是因爲一個正常的聲明不帶有綁定參數。如果我沒有綁定參數的查詢,它與原始JDBC和NamedJdbcTemplate的速度差不多。

我們的Oracle 11 g只需爲此查詢選擇一個錯誤的執行計劃,使用9個綁定參數並堅持使用它,而不管實際參數如何。我不知道爲什麼,也沒有真正的DBA可用。

一個PostgreSQL數據庫9.3以相同的數據上的試驗表明,它具有和不綁定參數同樣快;與一個開箱即用的Ubuntu安裝。