2014-09-15 3 views
2

Я выполняю следующий набор инструкций в своем приложении java. Он подключается к базе данных оракула.Улучшение производительности JDBC

stat=connection.createStatement(); 
stat1=commection.createstatement(); 
ResultSet rs = stat.executeQuery(BIGQUERY); 
while(rs.next()) { 
    obj1.setAttr1(rs.getString(1)); 
    obj1.setAttr2(rs.getString(1)); 
    obj1.setAttr3(rs.getString(1)); 
    obj1.setAttr4(rs.getString(1)); 

    ResultSet rs1 = stat1.executeQuery(SMALLQ1); 
    while(rs1.next()) { 
     obj1.setAttr5(rs1.getString(1)); 
    } 

    ResultSet rs2 = stat1.executeQuery(SMALLQ2); 
    while(rs2.next()) { 
     obj1.setAttr6(rs2.getString(1)); 

    } 
     . 
     . 
     . 
LinkedBlockingqueue.add(obj1); 
} 
//all staements and connections close 

BIGQUERY В возвращаюсь около 4,5 миллионов записей и для каждой записи, я должен выполнить меньшие запросы, которые в количестве 14. Каждый маленький запрос имеет три внутренних оператора объединения.

Мое многопоточное приложение теперь может обрабатывать 90 000 за один час. Но мне, возможно, придется ежедневно запускать код, поэтому я хочу обработать все записи за 20 часов. Я использую около 200 потоков, которые обрабатывают вышеуказанный код и сохраняют записи в связанной блокирующей очереди.

Увеличивает количество потоков вслепую, помогает повысить производительность или есть ли другой способ, с помощью которого я могу увеличить производительность наборов результатов?

PS: Я не могу опубликовать запрос здесь, но я уверен, что все запросы оптимизированы.

+1

Почему вы внедряете соединение в Java? База данных будет делать это гораздо эффективнее. –

+1

Прошу прощения, должно было быть ясно. BIGQUERY - это оператор sql, который имеет соединения, и я просто выполняю его. – user2133404

+0

, если вы можете заменить это процедурой оракула, вы сэкономите время, когда вы передаете данные с сервера на ваш код. – Leo

ответ

2

Чтобы улучшить производительность JDBC для вашего сценария, вы можете применить некоторые изменения.

Как вы увидите, все эти изменения могут значительно ускорить выполнение вашей задачи.

1. Использование пакетных операций.

Вы можете прочитать свой большой запрос и сохранить результаты в каком-то буфере. И только когда буфер заполнен, вы должны запустить подзапрос для всех данных, собранных в буфере. Это значительно сокращает количество выполняемых SQL-операторов.

static final int BATCH_SIZE = 1000; 
List<MyData> buffer = new ArrayList<>(BATCH_SIZE); 

while (rs.hasNext()) { 

    MyData record = new MyData(rs.getString(1), ..., rs.getString(4)); 
    buffer.add(record); 

    if (buffer.size() == BATCH_SIZE) { 
    processBatch(buffer); 
    } 
} 

void processBatch(List<MyData> buffer) { 

    String sql = "select ... where X and id in (" + getIDs(buffer) + ")"; 
    stat1.executeQuery(sql); // query for all IDs in buffer 
    while(stat1.hasNext()) { ... } 
    ... 
} 

2. Использование эффективных карт для хранения контента из многих выбирает.

Если ваши записи не столь велики, вы можете хранить их все сразу за 4 миллионами таблиц.

Я использовал этот подход много раз для ночных процессов (без обычных пользователей). Такой подход может потребовать много памяти кучи (то есть 100 МБ - 1 ГБ), но намного быстрее, чем подход 1).

Для этого вам нужна эффективная реализация карты, т. Е. - gnu.trove.map.TIntObjectMap (и т. Д.) , что намного лучше, чем стандартная библиотека Java.

final TIntObjectMap<MyData> map = new TIntObjectHashMap<MyData>(10000, 0.8f); 

// query 1 
while (rs.hasNext()) { 
    MyData record = new MyData(rs.getInt(1), rs.getString(2), ..., rs.getString(4)); 
    map.put(record.getId(), record); 
} 

// query 2 
while (rs.hasNext()) { 
    int id = rs.getInt(1); // my data id 
    String x = rs.getString(...); 
    int y = rs.getInt(...); 

    MyData record = map.get(id); 
    record.add(new MyDetail(x,y)); 
} 

// query 3 
// same pattern as query 2 

После этого у вас есть карта, заполненная всеми собранными данными. Вероятно, выделено много памяти. Вот почему вы можете использовать этот метод, только если у вас есть такие ресурсы.

Другая тема - как написать классы MyData и MyDetail как можно меньше. Вы можете использовать некоторые приемы:

  1. хранения 3 целых чисел (с ограниченным диапазоном) в 1 длинной переменной (используя Util для битового сдвига)
  2. хранения Дата объекты как целое число (YYMMDD)
  3. вызова ул.стажер() для каждой строки извлеченной из БД

3. Сделки

Если вы должны сделать некоторые обновления или вставки, чем 4 млн записей слишком много, чтобы обращаться в о сделках. Это слишком много для большинства конфигураций баз данных. Использовать подход 1) и совершить транзакцию для каждой партии. На каждой новой вставленной записи вы можете иметь что-то вроде RUN_ID, и если все будет хорошо, вы можете пометить этот идентификатор RUN_ID как успешный.

Если ваши запросы читаются - проблем нет. Однако вы можете пометить транзакцию как «Только для чтения», чтобы помочь вашей базе данных.

4. Размер выборки Jdbc.

При загрузке большого количества записей из базы данных очень важно установить правильный размер выборки в вашем соединении jdbc. Это уменьшает количество физических ударов в гнездо базы данных и ускоряет процесс.

Пример:

// jdbc 
statement.setFetchSize(500); 

// spring  
JdbcTemplate jdbc = new JdbcTemplate(datasource); 
jdbc.setFetchSize(500); 

Здесь вы можете найти некоторые тесты и шаблоны для использования размер выборки:

http://makejavafaster.blogspot.com/2015/06/jdbc-fetch-size-performance.html

5. PreparedStatement

Использование PreparedStatement, а не Утверждение.

6. Количество операторов sql.

Всегда старайтесь минимизировать количество операторов sql, которые вы отправляете в базу данных.

+0

Я категорически не согласен с # 3 - в Oracle почти всегда быстрее делать все в одной транзакции, а не разделить ее на более мелкие куски. Oracle не является SQL Server. –

+1

@a_horse_with_no_name - да, вы правы, большинство задач Oracle делают лучше в одном TX, но есть некоторые случаи, когда эта задача слишком велика для текущей конфигурации REDO LOG, и я говорил о таком случае. В этом случае я обычно разделяю задачу на меньшие TX с логическим маркером. В любом случае, я энтузиаст Oracle :) –

+0

Ограничения на размер журнала повтора не имеют ничего общего с производительностью. И поскольку вся проблема (только) о запросах, а не DML, журналы повторения почти наверняка не будут проблемой здесь. –

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