Столбец / Строка
... Мне не нужно поддерживать целостность транзакций через
вся операция, потому что я знаю, что столбец, который я изменяю,
не будет записываться или читаться во время обновления.
Любой UPDATE
в Модель MVCC PostgreSQL записывает новую версию всю строку . Если параллельные транзакции изменяют любой столбец той же строки, возникают проблемы с параллелизмом, требующие много времени. Подробности в руководстве. Знание того же столбца не будет затронуто одновременными транзакциями, избегает некоторых возможных осложнений, но не других.
Индекс
Чтобы не отвлекаться на оффтоп, давайте предположим, что
все значения статуса для 35 миллионов столбцов на данный момент установлены
к тому же (ненулевому) значению, что делает индекс бесполезным.
При обновлении всей таблицы (или ее основных частей) Postgres никогда не использует индекс . Последовательное сканирование выполняется быстрее, когда необходимо прочитать все или большинство строк. Напротив: обслуживание индекса означает дополнительные расходы на UPDATE
.
Performance
Например, допустим, у меня есть таблица с названием "заказы" с 35 миллионами
строки, и я хочу сделать это:
UPDATE orders SET status = null;
Я понимаю, что вы стремитесь к более общему решению (см. Ниже). Но по адресу актуальный вопрос спросил: это можно решить за всего за миллисекунды , независимо от размера таблицы:
ALTER TABLE orders DROP column status
, ADD column status text;
По документации:
Когда столбец добавляется с ADD COLUMN
, все существующие строки в таблице
инициализируются значением столбца по умолчанию (NULL
, если нет DEFAULT
оговорка уточняется). Если предложение DEFAULT отсутствует, это просто изменение метаданных ...
И
Форма DROP COLUMN
физически не удаляет столбец, а просто
делает его невидимым для операций SQL. Последующая вставка и обновление
Операции в таблице будут хранить нулевое значение для столбца. Таким образом,
удаление столбца происходит быстро, но не сразу
размер таблицы на диске, так как пространство, занимаемое удаленным
колонна не исправлена. Пространство будет исправлено со временем как
существующие строки обновляются. (Эти заявления не применяются, когда
сброс системы столбца oid; это делается с немедленной перепиской.)
Убедитесь, что у вас нет объектов в зависимости от столбца (ограничения внешнего ключа, индексы, представления, ...). Вы должны были бы удалить / воссоздать их. За исключением этого, крошечные операции над таблицей системного каталога pg_attribute
выполняют свою работу. Требуется эксклюзивная блокировка на столе, которая может быть проблемой для большой параллельной нагрузки. Поскольку это займет всего несколько миллисекунд, у вас все должно быть в порядке.
Если у вас есть столбец по умолчанию, который вы хотите сохранить, добавьте его обратно в отдельной команде . Выполнение одной и той же команды немедленно применило бы ее ко всем строкам, аннулировав эффект. Затем вы можете обновить существующие столбцы в пакетах .
Перейдите по ссылке на документацию и прочитайте Примечания в руководстве.
Общее решение
dblink
было упомянуто в другом ответе. Это позволяет получить доступ к «удаленным» базам данных Postgres в неявных отдельных подключениях. «Удаленная» база данных может быть текущей, тем самым достигая «автономных транзакций» : то, что записывает функция в «удаленной» БД, фиксируется и не может быть откатлено.
Это позволяет запускать одну функцию, которая обновляет большую таблицу на более мелкие части, и каждая часть фиксируется отдельно. Позволяет избежать накладных расходов на транзакции для очень большого числа строк и, что более важно, снимает блокировки после каждой части. Это позволяет выполнять параллельные операции без большой задержки и снижает вероятность взаимных блокировок.
Если у вас нет одновременного доступа, это вряд ли полезно - кроме как избежать ROLLBACK
после исключения. Также рассмотрим SAVEPOINT
для этого случая.
Ответственность
Прежде всего, множество мелких транзакций на самом деле дороже. Это имеет смысл только для больших таблиц . Сладость зависит от многих факторов.
Если вы не уверены, что делаете: безопасным методом является одна транзакция . Чтобы это работало должным образом, параллельные операции на столе должны совпадать. Например: одновременная запись пишет может переместить строку в раздел, который предположительно уже обработан. Или одновременное чтение может увидеть противоречивые промежуточные состояния. Вы были предупреждены.
Пошаговые инструкции
Сначала необходимо установить дополнительный модуль dblink:
Настройка соединения с dblink очень сильно зависит от настройки вашего кластера БД и действующих политик безопасности. Это может быть сложно. Связанный позже ответ с более , как связаться с dblink :
Создайте FOREIGN SERVER
и USER MAPPING
в соответствии с инструкциями, чтобы упростить и упростить соединение (если у вас его уже нет).
Предполагая serial PRIMARY KEY
с или без некоторых пробелов.
CREATE OR REPLACE FUNCTION f_update_in_steps()
RETURNS void AS
$func$
DECLARE
_step int; -- size of step
_cur int; -- current ID (starting with minimum)
_max int; -- maximum ID
BEGIN
SELECT INTO _cur, _max min(order_id), max(order_id) FROM orders;
-- 100 slices (steps) hard coded
_step := ((_max - _cur) / 100) + 1; -- rounded, possibly a bit too small
-- +1 to avoid endless loop for 0
PERFORM dblink_connect('myserver'); -- your foreign server as instructed above
FOR i IN 0..200 LOOP -- 200 >> 100 to make sure we exceed _max
PERFORM dblink_exec(
$$UPDATE public.orders
SET status = 'foo'
WHERE order_id >= $$ || _cur || $$
AND order_id < $$ || _cur + _step || $$
AND status IS DISTINCT FROM 'foo'$$); -- avoid empty update
_cur := _cur + _step;
EXIT WHEN _cur > _max; -- stop when done (never loop till 200)
END LOOP;
PERFORM dblink_disconnect();
END
$func$ LANGUAGE plpgsql;
Звоните:
SELECT f_update_in_steps();
Вы можете параметризовать любую часть в соответствии с вашими потребностями: имя таблицы, имя столбца, значение, ... просто обязательно очистите идентификаторы, чтобы избежать внедрения SQL:
Об избежании пустого ОБНОВЛЕНИЯ: