Sloupec / řádek
... Nepotřebuji, aby byla transakční integrita udržována během celé operace, protože vím, že sloupec, který měním, nebude během aktualizace zapsán ani přečten.
Jakékoli UPDATE
v modelu MVCC PostgreSQL zapíše novou verzi celého řádku . Pokud se souběžné transakce změní jakékoli sloupec stejného řádku, vznikají časově náročné problémy souběžnosti. Podrobnosti v návodu. Znáte stejný sloupec nebude dotčen souběžnými transakcemi vyhýbá se některým možné komplikace, ale ne jiné.
Index
Abychom se vyhnuli odklonu k diskuzi mimo téma, předpokládejme, že všechny hodnoty stavu pro 35 milionů sloupců jsou aktuálně nastaveny na stejnou (ne nulovou) hodnotu, takže index bude zbytečný.
Při aktualizaci celé tabulky (nebo jeho hlavní části) Postgres nikdy nepoužívá index . Sekvenční skenování je rychlejší, když je třeba přečíst všechny nebo většinu řádků. Naopak:Údržba indexu znamená dodatečné náklady na UPDATE
.
Výkon
Řekněme například, že mám tabulku s názvem „objednávky“ s 35 miliony řádků a chci udělat toto:
UPDATE orders SET status = null;
Chápu, že se snažíte o obecnější řešení (viz níže). Ale abych odpověděl na skutečnou otázku zeptal se:To lze vyřešit během několika milisekund , bez ohledu na velikost tabulky:
ALTER TABLE orders DROP column status
, ADD column status text;
Manuál (až do Postgres 10):
Když je sloupec přidán pomocí
ADD COLUMN
, všechny existující řádky v tabulce jsou inicializovány s výchozí hodnotou sloupce (NULL
pokud neníDEFAULT
doložka je specifikována). Pokud neexistujeDEFAULT
klauzule, jde pouze o změnu metadat [...]
Manuál (od Postgres 11):
Když je sloupec přidán pomocí
ADD COLUMN
a energeticky nezávisléDEFAULT
Pokud je zadáno, výchozí hodnota se vyhodnotí v době příkazu a výsledek se uloží do metadat tabulky. Tato hodnota bude použita pro sloupec pro všechny existující řádky. Pokud neníDEFAULT
je zadáno, použije se NULL. Ani v jednom případě není nutný přepis tabulky.Přidání sloupce s nestálým
DEFAULT
nebo změna typu existujícího sloupce bude vyžadovat přepsání celé tabulky a jejích indexů. [...]
A:
DROP COLUMN
formulář fyzicky neodstraní sloupec, ale jednoduše jej zneviditelní pro operace SQL. Následné operace vložení a aktualizace v tabulce uloží pro sloupec nulovou hodnotu. Vypuštění sloupce je tedy rychlé, ale nezmenší okamžitě velikost vaší tabulky na disku, protože místo obsazené zrušeným sloupcem není znovu získáno. Prostor bude časem získán zpět, protože existující řádky budou aktualizovány.
Ujistěte se, že nemáte objekty závislé na sloupci (omezení cizího klíče, indexy, pohledy, ...). Budete je muset zahodit/znovu vytvořit. Kromě toho drobné operace s tabulkou systémového katalogu pg_attribute
dělat tu práci. Vyžaduje exkluzivní zámek na stole, což může být problém při velkém souběžném zatížení. (Jako zdůrazňuje Buurman ve svém komentáři.) Nehledě na to je operace otázkou milisekund.
Pokud máte výchozí sloupec, který chcete zachovat, přidejte jej zpět v samostatném příkazu . Pokud to uděláte stejným příkazem, okamžitě to použijete na všechny řádky. Viz:
- Přidat nový sloupec bez zámku tabulky?
Chcete-li výchozí nastavení skutečně použít, zvažte to v dávkách:
- Optimalizuje PostgreSQL přidávání sloupců s jinými než NULL DEFAULT?
Obecné řešení
dblink
bylo zmíněno v jiné odpovědi. Umožňuje přístup ke „vzdáleným“ databázím Postgres v implicitních samostatných připojeních. "Vzdálená" databáze může být aktuální, čímž se dosáhne "autonomních transakcí" :to, co funkce zapíše do "vzdálené" databáze, je potvrzeno a nelze to vrátit zpět.
To umožňuje spustit jedinou funkci, která aktualizuje velkou tabulku po menších částech a každá část je potvrzena samostatně. Zabraňuje hromadění transakční režie u velmi velkého počtu řádků a co je důležitější, uvolňuje zámky po každé části. To umožňuje souběžným operacím pokračovat bez velkého zpoždění a snižuje pravděpodobnost uváznutí.
Pokud nemáte souběžný přístup, je to sotva užitečné – kromě toho, že se chcete vyhnout ROLLBACK
po výjimce. Zvažte také SAVEPOINT
pro ten případ.
Odmítnutí odpovědnosti
Za prvé, spousta malých transakcí je ve skutečnosti dražší. Tomá smysl pouze u velkých stolů . Sladká tečka závisí na mnoha faktorech.
Pokud si nejste jisti, co děláte:bezpečnou metodou je jedna transakce . Aby to fungovalo správně, musí se souběžné operace na stole odehrávat. Například:souběžné zápisy může přesunout řádek do oddílu, který je údajně již zpracován. Nebo souběžná čtení mohou vidět nekonzistentní přechodné stavy. Byli jste varováni.
Pokyny krok za krokem
Nejprve je třeba nainstalovat přídavný modul dblink:
- Jak používat (instalovat) dblink v PostgreSQL?
Nastavení připojení s dblink velmi závisí na nastavení vašeho DB clusteru a platných bezpečnostních zásadách. Může to být složité. Související pozdější odpověď s dalšími informacemi, jak se připojit k dblink :
- Trvalé vkládání do UDF, i když se funkce přeruší
Vytvořte FOREIGN SERVER
a USER MAPPING
jak je tam uvedeno pro zjednodušení a zefektivnění připojení (pokud jej již nemáte).
Za předpokladu serial PRIMARY KEY
s nebo bez mezer.
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;
Volejte:
SELECT f_update_in_steps();
Libovolnou část můžete parametrizovat podle svých potřeb:název tabulky, název sloupce, hodnotu, ... jen nezapomeňte dezinfikovat identifikátory, abyste se vyhnuli vkládání SQL:
- Název tabulky jako parametr funkce PostgreSQL
Vyhněte se prázdným UPDATES:
- Jak mohu (nebo mohu) SELECT DISTINCT na více sloupcích?