Почему PostgreSQL не сохраняет на диск при длительной операции INSERT… SELECT…? - PullRequest
0 голосов
/ 04 декабря 2018

Я использую стороннюю оболочку данных для перемещения большого куска данных (с очень простым преобразованием даты в одном столбце) в локальную базу данных.Используя курсор Django (потому что мне лень извлекать учетные данные для создания необработанного курсора psycopg2), я делаю запрос такого типа (анонимный и с удалением пары соединений, но в остальном идентичный оригиналу):

cursor.executemany(
    sql.SQL(
        """
        INSERT INTO local_table (
            foreign_key_id,
            other_foreign_key_id,
            datetime,
            comment
        )
        SELECT other_local_table.id,
               %s,
               (object_date + to_timestamp(object_time, 'HH24:MI')::time) at time zone '…',
               comment
          FROM imported_schema.remote_table
          JOIN other_local_table ON other_local_table.code = remote_table.code
        """
    ),
    [(dummy_id,)],
)

Тем не менее, локальный Postgres-сервер через некоторое время всегда убивает OOM .Я ожидал, что Postgres сбросит новые строки на диск, чтобы избежать нехватки памяти, но, насколько я могу судить, этого просто не происходит - /var/lib/docker/volumes/vagrant_postgres_data увеличивается только на несколько МБ, в то время как резидентное использование памяти превращается в ГБ.На локальном сервере недостаточно оперативной памяти для хранения всего набора результатов в памяти, поэтому мне нужно решение, которое не требует более дорогой настройки оборудования.

Нужно ли устанавливать что-то вроде wal_sync_method илиwork_mem чтобы это работало?


Согласно документам executemany должно быть правильным инструментом для работы:

Эта функция в основном полезна для команд, которыеобновите базу данных: любой набор результатов, возвращенный запросом, отбрасывается.


Запуск Postgres 10.6 контейнеров в Linux на обоих серверах и в Django 2.1 локально.Я не использую никаких расширений, кроме FDW.


Объясните план:

Insert on local_table  (cost=817872.44..818779.47 rows=25915 width=56)
  ->  Subquery Scan on "*SELECT*"  (cost=817872.44..818779.47 rows=25915 width=56)
        ->  HashAggregate  (cost=817872.44..818390.74 rows=25915 width=48)
              Group Key: other_local_table.id, 1, timezone('…'::text, (remote_table.object_date + (to_timestamp((remote_table.object_time)::text, 'HH24:MI'::text))::time without time zone)), remote_table.comment
              ->  Nested Loop  (cost=101.15..807974.88 rows=989756 width=48)
                    ->  Nested Loop  (cost=0.57..60.30 rows=73 width=12)
                          ->  Nested Loop  (cost=0.29..42.35 rows=38 width=4)
                                ->  Seq Scan on fourth_local_table  (cost=0.00..7.45 rows=1 width=4)
                                      Filter: ((code)::text = '…'::text)
                                ->  Index Scan using … on third_local_table  (cost=0.29..34.49 rows=41 width=8)
                                      Index Cond: (id = fourth_local_table.id)
                          ->  Index Scan using … on other_local_table  (cost=0.29..0.45 rows=2 width=16)
                                Index Cond: (id = third_local_table.id)
                    ->  Foreign Scan on remote_table  (cost=100.58..9421.44 rows=151030 width=20)

postgresqltuner предлагает I

set vm.overcommit_memory = 2 в /etc/sysctl.conf… Это отключит чрезмерное использование памяти и предотвратит уничтожение postgresql убийцей OOM.

Это решение?

1 Ответ

0 голосов
/ 05 декабря 2018

Я не вижу ничего в вашем плане выполнения, кроме HashAggregate, который мог бы потреблять любой объем памяти, и который должен быть ограничен work_mem.

Чтобы диагностировать это, вы должны сначала настроитьваша система, так что вы получаете обычную ошибку OOM вместо вызова убийцы OOM.Это означает установить vm.overcommit_memory = 2 с помощью sysctl и настроить vm_overcommit_ratio на 100 * (RAM - swap) / RAM.

Когда сервер получит ошибку OOM, он сбросит текущие контексты памяти и ихразмер в журнал PostgreSQL.Это должно дать указание, куда идет память.Добавьте его к вопросу в случае сомнений.

Используете ли вы сторонние расширения?

...