2016-05-26 3 views
1

Я использую Oracle 11g. У меня есть 3 таблицы (A,B,C) в моей базе данных A <one-many> B <many-one> C У меня есть фрагмент кода, который выполняет три вставки: во-первых, в A и C, после чего в B. Этот фрагмент кода выполняется много раз (200000) и делает операции ввода в 200000 в каждой таблице.Spring jdbcTemplate vs PreparedStatement. Разница в производительности

У меня есть два способа сделать вставку:

  1. JDBC PreparedStatement:

    DataSource ds = jdbcTemplate.getDataSource(); 
    
    try (Connection connection = ds.getConnection(); 
        PreparedStatement statement = connection.prepareStatement(sql1); 
        PreparedStatement statement2 = connection.prepareStatement(sql2); 
        PreparedStatement statement3 = connection.prepareStatement(sql3);) { 
    
        connection.setAutoCommit(false); 
        final int batchSize = 20; 
        int count = 0; 
    
        for (int i=1; i<= total; i++) { 
    
         // Define sql parameters 
    
         statement.setString(1, p1); 
    
         statement2.setString(1, p2); 
         statement2.setString(2, p3); 
    
         statement3.setInt(1, p4); 
         statement3.setString(2, p5); 
    
         statement.addBatch(); 
         statement2.addBatch(); 
         statement3.addBatch(); 
    
         if (++count % batchSize == 0) { 
            statement.executeBatch(); 
            statement.clearBatch(); 
            statement2.executeBatch(); 
            statement2.clearBatch(); 
            statement3.executeBatch(); 
            statement3.clearBatch(); 
            connection.commit(); 
            System.out.println(i); 
         } 
         } 
    
         statement.executeBatch(); 
         statement.clearBatch(); 
         statement2.executeBatch(); 
         statement2.clearBatch(); 
         statement3.executeBatch(); 
         statement3.clearBatch(); 
         connection.commit(); 
        } 
        catch (SQLException e) { 
         e.printStackTrace(); 
        } 
    } 
    
  2. Spring JdbcTemplate:

    List<String> bulkLoadRegistrationSql = new ArrayList<String>(20); 
    
    for (int i=1; i<= total; i++) { 
    
        // 1. Define sql parameters p1,p2,p,3p4,p5 
    
        // 2. Prepare sql using parameters from 1 
        String sql1String = ... 
        String sql2String = ... 
        String sql3String = ... 
    
        bulkLoadRegistrationSql.add(sql1String); 
        bulkLoadRegistrationSql.add(sql2String); 
        bulkLoadRegistrationSql.add(sql3String); 
    
        if (i % 20 == 0) { 
          jdbcTemplate.batchUpdate(bulkLoadRegistrationSql 
           .toArray(new String[bulkLoadRegistrationSql.size()])); 
          //Clear inserted batch 
          bulkLoadRegistrationSql = new ArrayList<String>(20);  
        } 
    
    } 
    

Я измерил время выполнения total = 200000, и результаты очень сбивают меня с толку. Пружина jdbcTemplate выполнена в 1480 секунд, PreparedStatement в JDBC 200 секунд

Я смотрел в jdbcTemplate источник и нашел, что он использует Statement снизу, который должен быть менее эффективным, чем PreparedStatement. Однако разница в результатах слишком велика, и я не уверен, что это происходит только из-за разницы между Statement и PreparedStatement. Каковы ваши идеи по этому поводу? Если результаты теоретически равны, если jdbcTemplate заменен на namedParameterJdbcTemplate?

ответ

1

Да, это должно быть намного ближе, если большинство времени было потрачено на ожидание ответов из базы данных. У Spring есть свои накладные расходы, поэтому у вас будет больше потребления ресурсов на стороне клиента.

В подготовленном заявлении с использованием заполнителей Oracle только один раз анализирует SQL и генерирует план один раз. Затем он кэширует результаты анализа, а также план SQL. В вашем примере JDBCTemplate каждый оператор SQL выглядит иначе, чем синтаксический анализатор, поэтому для этого требуется полный анализ и генерация плана сервером. В зависимости от мощности вашего сервера Oracle это приведет к увеличению времени отклика для каждого оператора SQL. Для 200 000 операторов SQL чистое увеличение на 1280 секунд приводит к дополнительному 6,4 миллисекундам за звонок. Для меня это кажется разумным увеличением из-за необходимости дополнительного анализа.

Я предлагаю добавить некоторые данные синхронизации к вызовам базы данных, поэтому вы можете подтвердить, что время ответа SQL ниже в улучшенной версии.

0

Для любого заданного SQL от 50 до 80 процентов времени работы базы данных тратится на вычислительные пути доступа.

При непосредственном использовании PreparedStatement механизм базы данных вычисляет путь доступа один раз и возвращает дескриптор уже вычисленного пути доступа (на этапе «подготовить»). Когда вызывается подготовленный оператор, движок базы данных должен применять параметры только к уже подготовленному пути доступа и возвращать курсор.

0

Spring JDBCTemplate также имеет методы, которые используют подготовленный Statement. См. Это link Результаты теста имеют смысл - вы не можете сравнивать выполнение с помощью подготовленного оператора и обычного SQL.

Перегруженные методы пакетной обработки в Spring JDBCTemplate, которые используют подготовленные операторы, например, нижеследующую функцию.

ИНТ [] [] batchUpdate (String SQL, окончательный batchArgs Collection, окончательный INT BATCHSIZE, окончательный ParameterizedPreparedStatementSetter PSS)

Смежные вопросы