V poslední době se to několikrát objevilo, a to jak na SO, tak na e-mailových konferencích PostgreSQL.
TL;DR pro vaše poslední dva body:
(a) Větší sdílené vyrovnávací paměti mohou být důvodem, proč je TRUNCATE na serveru CI pomalejší. Na vině může být také odlišná konfigurace fsync nebo použití rotačních médií místo SSD.
(b) TRUNCATE
má pevnou cenu, ale ne nutně pomalejší než DELETE
a navíc to dá víc práce. Viz podrobné vysvětlení, které následuje.
AKTUALIZACE: Z tohoto příspěvku vznikla významná diskuse o výkonu pgsql. Viz toto vlákno.
AKTUALIZACE 2: Do 9.2beta3 byla přidána vylepšení, která by s tím měla pomoci, viz tento příspěvek.
Podrobné vysvětlení TRUNCATE
vs DELETE FROM
:
I když nejsem odborník na toto téma, chápu to tak, že TRUNCATE
má téměř fixní náklady na tabulku, zatímco DELETE
je alespoň O(n) pro n řádků; horší, pokud existují nějaké cizí klíče odkazující na odstraňovanou tabulku.
Vždy jsem předpokládal, že fixní náklady na TRUNCATE
byla nižší než cena DELETE
na téměř prázdném stole, ale to vůbec není pravda.
TRUNCATE table;
dělá více než DELETE FROM table;
Stav databáze po TRUNCATE table
je v podstatě stejné, jako kdybyste místo toho spustili:
DELETE FROM table;
VACCUUM (FULL, ANALYZE) table;
(pouze 9.0+, viz poznámka pod čarou)
... i když samozřejmě TRUNCATE
ve skutečnosti nedosahuje svých účinků pomocí příkazu DELETE
a VACUUM
.
Jde o to, že DELETE
a TRUNCATE
dělat různé věci, takže neporovnáváte jen dva příkazy se stejnými výsledky.
A DELETE FROM table;
umožňuje zachovat mrtvé řádky a nadýmání, umožňuje indexům přenášet mrtvé položky, neaktualizuje statistiky tabulky používané plánovačem dotazů atd.
A TRUNCATE
vám dává úplně novou tabulku a indexy, jako by byly jen CREATE
vyd. Jako byste smazali všechny záznamy, přeindexovali tabulku a provedli VACUUM FULL
.
Pokud vám nezáleží na tom, jestli v tabulce zbylo nahrubo, protože se chystáte ji znovu naplnit, možná bude lepší použít DELETE FROM table;
.
Protože nepoužíváte VACUUM
zjistíte, že mrtvé řádky a položky rejstříku se hromadí jako nadýmání, které musí být naskenováno a poté ignorováno; tím se zpomalí všechny vaše dotazy. Pokud vaše testy ve skutečnosti nevytvářejí a neodstraňují tolik dat, nemusíte si toho všimnout nebo vás to zajímat a vždy můžete provést VACUUM
nebo dva během zkušebního provozu, pokud ano. Raději nechte agresivní nastavení autovakua zajistit, že to autovakuum udělá za vás na pozadí.
Stále můžete TRUNCATE
všechny vaše stoly po celku běží testovací sada, aby se zajistilo, že se během mnoha běhů nevytvoří žádné efekty. Na 9.0 a novějších VACUUM (FULL, ANALYZE) table;
globálně na stole je přinejmenším stejně dobré, ne-li lepší, a je to mnohem jednodušší.
IIRC Pg má několik optimalizací, což znamená, že si může všimnout, že vaše transakce je jediná, která vidí tabulku, a okamžitě označit bloky jako volné. Při testování, když jsem chtěl vytvořit nadýmání, musel jsem mít více než jedno souběžné připojení, abych to udělal. Na to bych ale nespoléhal.
DELETE FROM table;
je velmi levná pro malé stoly bez f/k refů
Chcete-li DELETE
všechny záznamy z tabulky bez odkazů na cizí klíč, všechny Pg musí provést sekvenční skenování tabulky a nastavit xmax
z nalezených n-tic. Jedná se o velmi levnou operaci - v podstatě lineární čtení a semilineární zápis. AFAIK se nemusí dotýkat indexů; nadále ukazují na mrtvé n-tice, dokud je nevyčistí pozdější VACUUM
to také označí bloky v tabulce obsahující pouze mrtvé n-tice jako volné.
DELETE
zdraží pouze v případě, že jich je spousta záznamů, pokud existuje mnoho odkazů na cizí klíče, které je třeba zkontrolovat, nebo pokud spočítáte následující tabulku VACUUM (FULL, ANALYZE) table;
potřeba, aby odpovídala TRUNCATE
efekty v rámci nákladů na váš DELETE
.
V mých testech zde DELETE FROM table;
byl obvykle 4x rychlejší než TRUNCATE
při 0,5 ms oproti 2 ms. Toto je testovací databáze na SSD, běžící s fsync=off
protože je mi jedno, jestli o všechna tato data přijdu. Samozřejmě DELETE FROM table;
nedělá stejnou práci, a pokud budu pokračovat s tabulkou VACUUM (FULL, ANALYZE) table;
je to mnohem dražší 21 ms, takže DELETE
je výhra pouze v případě, že stůl ve skutečnosti nepotřebuji nedotčený.
TRUNCATE table;
dělá mnohem více práce s pevnými náklady a úklidu než DELETE
Naproti tomu TRUNCATE
musí udělat hodně práce. Musí alokovat nové soubory pro tabulku, její tabulku TOAST, pokud existuje, a každý index, který tabulka má. Do těchto souborů musí být zapsána záhlaví a systémové katalogy mohou vyžadovat aktualizaci (v tomto bodě si nejsem jistý, nekontrolováno). Poté musí nahradit staré soubory novými nebo odstranit staré a musí zajistit, aby souborový systém zachytil změny pomocí synchronizační operace - fsync() nebo podobně - která obvykle vyprázdní všechny vyrovnávací paměti na disk. . Nejsem si jistý, zda je synchronizace přeskočena, pokud používáte možnost (požírání dat) fsync=off
.
Nedávno jsem se dozvěděl, že TRUNCATE
musí také vyprázdnit všechny buffery PostgreSQL související se starou tabulkou. To může s obrovskými shared_buffers
zabrat netriviální množství času . Mám podezření, že to je důvod, proč je na vašem CI serveru pomalejší.
Zůstatek
Každopádně můžete vidět, že TRUNCATE
tabulky, která má přidruženou tabulku TOAST (většina má) a několik indexů, může chvíli trvat. Ne dlouhé, ale delší než DELETE
z téměř prázdného stolu.
V důsledku toho může být lepší udělat DELETE FROM table;
.
--
Poznámka:na databázích před verzí 9.0 CLUSTER table_id_seq ON table; ANALYZE table;
nebo tabulka VACUUM FULL ANALYZE table; REINDEX table;
by byl bližší ekvivalent TRUNCATE
. VACUUM FULL
impl se v 9.0 změnil na mnohem lepší.