Чтобы улучшить производительность 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 как можно меньше. Вы можете использовать некоторые приемы:
- хранения 3 целых чисел (с ограниченным диапазоном) в 1 длинной переменной (используя Util для битового сдвига)
- хранения Дата объекты как целое число (YYMMDD)
- вызова ул.стажер() для каждой строки извлеченной из БД
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, которые вы отправляете в базу данных.
Почему вы внедряете соединение в Java? База данных будет делать это гораздо эффективнее. –
Прошу прощения, должно было быть ясно. BIGQUERY - это оператор sql, который имеет соединения, и я просто выполняю его. – user2133404
, если вы можете заменить это процедурой оракула, вы сэкономите время, когда вы передаете данные с сервера на ваш код. – Leo