sql >> Databáze >  >> RDS >> PostgreSQL

Jak provedu velké neblokující aktualizace v PostgreSQL?

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 neexistuje DEFAULT 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?


  1. Poskytne UUID jako primární klíč v PostgreSQL špatný výkon indexu?

  2. Sledování na úrovni sloupců a řádků při slučovací replikaci

  3. Dotaz typu Rails typu dat Postgres JSON

  4. Oracle SQL – Identifikujte sekvenční rozsahy hodnot