Таким образом, выясняется, что суть проблемы заключается в том, что по умолчанию Postgres запускается в режиме «autoCommit», а также ему нужны / используются курсоры, чтобы иметь возможность «пролистывать» данные (например: читать первые 10K результатов). , затем следующий, затем следующий), однако курсоры могут существовать только внутри транзакции. Таким образом, по умолчанию всегда считываются все строки в ОЗУ, а затем разрешается вашей программе начать обработку «первой строки результата, затем второй» после того, как все это поступило, по двум причинам, это не в транзакции (поэтому курсоры не работает), а также размер выборки не был установлен.
Итак, как инструмент командной строки psql
достигает пакетного ответа (его установка FETCH_COUNT
) для запросов, заключается в том, чтобы «обернуть» свои запросы выбора в краткосрочную транзакцию (если транзакция еще не открыта), так что курсоры могут работать. Вы можете сделать что-то подобное и с JDBC:
static void readLargeQueryInChunksJdbcWay(Connection conn, String originalQuery, int fetchCount, ConsumerWithException<ResultSet, SQLException> consumer) throws SQLException {
boolean originalAutoCommit = conn.getAutoCommit();
if (originalAutoCommit) {
conn.setAutoCommit(false); // start temp transaction
}
try (Statement statement = conn.createStatement()) {
statement.setFetchSize(fetchCount);
ResultSet rs = statement.executeQuery(originalQuery);
while (rs.next()) {
consumer.accept(rs); // or just do you work here
}
} finally {
if (originalAutoCommit) {
conn.setAutoCommit(true); // reset it, also ends (commits) temp transaction
}
}
}
@FunctionalInterface
public interface ConsumerWithException<T, E extends Exception> {
void accept(T t) throws E;
}
Это дает преимущество в том, что требуется меньше оперативной памяти, и, по моим результатам, в целом она работает быстрее, даже если вам не нужно экономить оперативную память. Weird. Это также дает преимущество в том, что ваша обработка первой строки «начинается быстрее» (поскольку она обрабатывает страницу за раз).
А вот как это сделать «курсором необработанного postgres» вместе с полным демо кодом , хотя в моих экспериментах казалось, что способ JDBC, приведенный выше, был несколько быстрее по любой причине.
Другим вариантом будет отключение режима autoCommit
везде, хотя вам все равно придется всегда вручную указывать fetchSize для каждого нового оператора (или вы можете установить размер выборки по умолчанию в строке URL).