Как передавать потоком данные, отсортированные из таблицы в PostgreSQL?

#postgresql #jdbc #query-performance

#postgresql #jdbc #запрос-производительность

Вопрос:

Я пытаюсь получить данные из таблицы PostgreSQL в отсортированном порядке, используя Java. Проблема заключается в планировании запросов PostgreSQL — взгляните на эти запросы:

 select *
from the_table
order by the_indexed_column asc
;
  

План запроса для этого таков:

 Gather Merge  (cost=16673025.39..28912422.53 rows=104901794 width=64)
  Workers Planned: 2
  ->  Sort  (cost=16672025.36..16803152.60 rows=52450897 width=64)
        Sort Key: "time"
        ->  Parallel Seq Scan on raw  (cost=0.00..4030550.63 rows=52450897 width=64)
  

Sort Вверху предотвращает потоковую передачу данных, потому что сначала они должны агрегироваться. Это проблематично для сортировок с большими объемами данных, например, 20 ГБ в моем случае, поскольку они должны быть сохранены на диске.

Сравните этот запрос:

 select *
from raw
order by the_index_column asc
limit 10000000
;
  

План:

 Limit  (cost=0.57..9871396.70 rows=10000000 width=64)
  ->  Index Scan using raw_time_idx on raw  (cost=0.57..124263259.38 rows=125882152 width=64)
  

Эти данные можно легко передавать потоком.

Я думаю, что PostgreSQL оптимизирует здесь только общую скорость запросов, а не дополнительные функции, такие как использование диска и возможности потоковой передачи. Есть ли способ настроить PostgreSQL так, чтобы он выбирал второй план в пользу первого?

РЕДАКТИРОВАТЬ: Это код для выполнения запроса. Строка в конце не печатается.

 Connection database = DriverManager.getConnection(DatabaseConstants.DATABASE_URL, DatabaseConstants.USER, DatabaseConstants.PASSWORD);
String sql = "select "  
                "column_a, column_b, some_expression, morestuff "  
                "from the_table "  
                "order by the_indexed_column asc "  
                ";";
database.setAutoCommit(false);
PreparedStatement statement = database.prepareStatement(sql);
statement.setFetchSize(1024);
ResultSet set = statement.executeQuery();
System.out.println("Got first results...");
  

Значение cursor_tuple_fraction было снижено до 0.05, 0.01 и 0.0 без какого-либо эффекта.

Версия PostgreSQL: 10.7, версия драйвера: 42.2.5.jre7 (самая последняя в Maven (теперь по-настоящему)), ОС: Fedora 29 (минимальная, с KDE сверху)

Это вывод в журнале с log_min_duration_statement = 0 :

 2019-03-29 17:11:52.532 CET [15068] LOG:  database system is ready to accept connections
2019-03-29 17:12:04.615 CET [15119] LOG:  duration: 0.397 ms  parse <unnamed>: SET extra_float_digits = 3
2019-03-29 17:12:04.615 CET [15119] LOG:  duration: 0.008 ms  bind <unnamed>: SET extra_float_digits = 3
2019-03-29 17:12:04.615 CET [15119] LOG:  duration: 0.046 ms  execute <unnamed>: SET extra_float_digits = 3
2019-03-29 17:12:04.615 CET [15119] LOG:  duration: 0.024 ms  parse <unnamed>: SET application_name = 'PostgreSQL JDBC Driver'
2019-03-29 17:12:04.615 CET [15119] LOG:  duration: 0.006 ms  bind <unnamed>: SET application_name = 'PostgreSQL JDBC Driver'
2019-03-29 17:12:04.615 CET [15119] LOG:  duration: 0.026 ms  execute <unnamed>: SET application_name = 'PostgreSQL JDBC Driver'
2019-03-29 17:12:04.662 CET [15119] LOG:  duration: 0.023 ms  parse <unnamed>: BEGIN
2019-03-29 17:12:04.662 CET [15119] LOG:  duration: 0.006 ms  bind <unnamed>: BEGIN
2019-03-29 17:12:04.662 CET [15119] LOG:  duration: 0.004 ms  execute <unnamed>: BEGIN
2019-03-29 17:12:04.940 CET [15119] LOG:  duration: 277.705 ms  parse <unnamed>: [the query...] 
2019-03-29 17:12:05.162 CET [15119] LOG:  duration: 222.742 ms  bind <unnamed>/C_1: [the query...]
  

При этом увеличивается использование диска.

Комментарии:

1. Не связано, но: 9.1-901-1.jdbc4 ужасно устарело. Текущая версия драйвера — 42.2.5, которая также доступна в Maven Central

2. О, я искал только версии пакета postgresql группы postgresql. Не заметил, что текущая группа — org.postgresql. Обновил драйвер без эффекта.

Ответ №1:

Это не должно быть проблемой. Используйте курсор, применяя setFetchSize к подготовленному оператору ненулевое значение.

Затем PostgreSQL выберет план, который быстро возвращает первые строки, то есть сканирование индекса.

Если PostgreSQL все еще выбирает сортировку, уменьшите cursor_tuple_fraction значение по умолчанию, равное 0.1 (10% от общего набора результатов).

Для записи: вот как это должно выглядеть в журнале:

 duration: 0.126 ms  parse S_1: BEGIN
duration: 0.015 ms  bind S_1: BEGIN
duration: 0.034 ms  execute S_1: BEGIN
duration: 0.998 ms  parse S_2: SELECT /* the query */
duration: 1.752 ms  bind S_2/C_3: SELECT /* the query */
duration: 0.081 ms  execute S_2/C_3: SELECT /* the query */
duration: 0.060 ms  execute fetch from S_2/C_3: SELECT /* the query */
duration: 0.065 ms  execute fetch from S_2/C_3: SELECT /* the query */
duration: 0.070 ms  execute fetch from S_2/C_3: SELECT /* the query */
duration: 0.078 ms  execute fetch from S_2/C_3: SELECT /* the query */
  

Комментарии:

1. Добавлена информация к моему вопросу, поскольку ваше решение у меня не сработало :/

2. Можете ли вы log_min_duration_statement = 0 включить postgresql.conf и посмотреть, что регистрируется?

3. Добавлены записи в журнале.

4. Я добавил, как должны выглядеть записи журнала. Вы не видите execute сообщений для запроса?

5. Это все, что я вижу через 10 минут (на недавнем ноутбуке).