Медленный простой запрос на обновление базы данных PostgreSQL с 3 миллионами строк - PullRequest
27 голосов
/ 29 июля 2010

Я пробую простое UPDATE table SET column1 = 0 для таблицы с ~ 3 миллионами строк в Postegres 8.4, но для ее завершения требуется вечность. Он работает более 10 минут. теперь в моей последней попытке.

Раньше я пытался выполнить команды VACUUM и ANALYZE для этой таблицы, а также пытался создать некоторые индексы (хотя я сомневаюсь, что это будет иметь какое-то значение в этом случае), но ни одна из них не помогает.

Есть еще идеи?

Спасибо, Рикардо

Обновление:

Это структура таблицы:

CREATE TABLE myTable
(
  id bigserial NOT NULL,
  title text,
  description text,
  link text,
  "type" character varying(255),
  generalFreq real,
  generalWeight real,
  author_id bigint,
  status_id bigint,
  CONSTRAINT resources_pkey PRIMARY KEY (id),
  CONSTRAINT author_pkey FOREIGN KEY (author_id)
      REFERENCES users (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT c_unique_status_id UNIQUE (status_id)
);

Я пытаюсь запустить UPDATE myTable SET generalFreq = 0;

Ответы [ 9 ]

32 голосов
/ 17 июля 2014

Мне нужно обновить таблицы из 1 или 2 миллиардов строк с различными значениями для каждой строки.Каждый прогон вносит ~ 100 миллионов изменений (10%).Моя первая попытка заключалась в том, чтобы сгруппировать их в транзакции по 300 тыс. Обновлений непосредственно в конкретном разделе, поскольку Postgresql не всегда оптимизирует подготовленные запросы, если вы используете разделы.myId = id "
Дает 1500 обновлений / сек.это означает, что каждый запуск займет не менее 18 часов.

ГОРЯЧИЕ обновления, как описано здесь, с FILLFACTOR = 50.Дает 1600 обновлений / сек.Я использую твердотельные накопители, поэтому это дорогостоящее улучшение, поскольку оно удваивает размер хранилища. Вставьте во временную таблицу обновленного значения и объедините их после с помощью UPDATE ... FROM Дает 18 000 обновлений / сек.,если я сделаю ВАКУУМ для каждого раздела;100000 up / s в противном случае.Cooool.
Вот последовательность операций:
CREATE TEMP TABLE tempTable (id BIGINT NOT NULL, field(s) to be updated,
CONSTRAINT tempTable_pkey PRIMARY KEY (id));

Накапливать кучу обновлений в буфере в зависимости от доступной оперативной памяти, когда она заполнена, или необходимо изменить таблицу /раздел или завершен:

COPY tempTable FROM buffer;
UPDATE myTable a SET field(s)=value(s) FROM tempTable b WHERE a.id=b.id;
COMMIT;
TRUNCATE TABLE tempTable;
VACUUM FULL ANALYZE myTable;

Это означает, что запуск теперь занимает 1,5 часа вместо 18 часов для 100 миллионов обновлений, включая вакуум.

13 голосов
/ 30 июля 2010

Посмотрите на этот ответ: PostgreSQL медленно работает на большой таблице с массивами и множеством обновлений

Сначала начните с лучшего FILLFACTOR, выполните VACUUM FULL, чтобы принудительно переписать таблицу, и проверьте HOT-обновления после вашего запроса UPDATE:

SELECT n_tup_hot_upd, * FROM pg_stat_user_tables WHERE relname = 'myTable';

ГОРЯЧИЕ обновления намного быстрее, когда у вас есть много записей для обновления. Более подробную информацию о HOT можно найти в этой статье .

Ps. Вам нужна версия 8.3 или лучше.

7 голосов
/ 29 июля 2010

После ожидания 35 мин.чтобы закончить мой запрос ОБНОВЛЕНИЯ (и все еще не сделал), я решил попробовать что-то другое.Итак, я сделал команду:

CREATE TABLE table2 AS 
SELECT 
  all the fields of table1 except the one I wanted to update, 0 as theFieldToUpdate
from myTable

Затем добавьте индексы, затем отбросьте старую таблицу и переименуйте новую, чтобы занять ее место.Это заняло всего 1,7 мин.обрабатывать плюс некоторое дополнительное время для воссоздания индексов и ограничений.Но это помогло!:)

Конечно, это сработало только потому, что никто другой не использовал базу данных.Мне нужно было бы сначала заблокировать таблицу, если бы это было в производственной среде.

2 голосов
/ 20 апреля 2016

Попробуйте (обратите внимание, что generalFreq начинается с типа REAL и остается прежним):

ALTER TABLE myTable ALTER COLUMN generalFreq TYPE REAL USING 0;

Это перезапишет таблицу, аналогично DROP + CREATE, и перестроит все индексы.Но все в одной команде.Гораздо быстрее (примерно в 2 раза), и вам не нужно иметь дело с зависимостями и воссозданием индексов и других вещей, хотя он блокирует таблицу (исключает доступ - т.е. полная блокировка) на время.Или, может быть, это то, что вы хотите, если вы хотите, чтобы все остальное стояло в очереди за ним.Если вы не обновляете «слишком много» строк, этот способ будет медленнее, чем просто обновление.

2 голосов
/ 26 февраля 2016

Сегодня я провел много часов с подобной проблемой. Я нашел решение : , чтобы удалить все ограничения / индексы перед обновлением . Независимо от того, проиндексирован ли обновляемый столбец или нет, похоже, что psql обновляет все индексы для всех обновленных строк. После завершения обновления добавьте ограничения / индексы обратно.

1 голос
/ 23 ноября 2017

Первое, что я бы предложил (из https://dba.stackexchange.com/questions/118178/does-updating-a-row-with-the-same-value-actually-update-the-row), - это обновить только те строки, которые «нуждаются» в этом, например:

 UPDATE myTable SET generalFreq = 0 where generalFreq != 0;

(может также потребоваться индекс для generalFreq). Тогда вы обновите меньше строк. Хотя это и не так, если все значения уже отличны от нуля, но обновление меньшего количества строк «может помочь», так как в противном случае оно обновляет их и все индексы независимо от того, изменилось ли значение или нет.

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

0 голосов
/ 15 ноября 2018

В своих тестах я заметил, что большое обновление, более 200 000 строк, медленнее, чем 2 обновления по 100 000 строк, даже с временной таблицей.

Мое решение состоит в цикле, в каждом цикле создайте временную таблицу из 200 000 строк, в этой таблице я вычисляю свои значения, затем обновляю основную таблицу новыми значениями aso ...

Каждые 2 000 000 строк, я вручную "VACUUM ANALYZE mytable", я заметил, что автоматический пылесос не выполняет свою работу для таких обновлений.

0 голосов
/ 11 августа 2010

try

UPDATE myTable SET generalFreq = 0.0;

Может быть, это проблема с кастингом

0 голосов
/ 29 июля 2010

Как у вас это работает? Если вы зацикливаете каждую строку и выполняете оператор обновления, вы запускаете потенциально миллионы отдельных обновлений, поэтому он будет работать невероятно медленно.

Если вы выполняете один оператор обновления для всех записей в одном операторе, он будет выполняться намного быстрее, и если этот процесс медленный, то, вероятно, он зависит от вашего оборудования больше, чем что-либо еще. 3 миллиона - это много записей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...