Výkon partitioningu přes triggery a rules

V PostgreSQL existují dva koncepčně zcela odlišné způsoby jak řešit partitioning - přes rules a přes triggery. Jedním z důvodů využití partitioningu je často i výkon, takže se podívejme jak se tyto dvě cesty liší právě ohledně výkonu.

V tomto orientačním benchmarku jsou zkoumány následující tři způsoby partitioningu:

  • trigger s dynamickými SQL dotazy (EXECUTE)
  • trigger bez dynamických SQL dotazů
  • rules

Celý test je velmi jednoduchý - je vytvořena tabulka s daným počtem podřízených tabulek (partitions), a je do ní vloženo 500.000 záznamů. Počet partitions se mění aby bylo možno posoudit jak ovlivňuje náročnost jednotlivých řešení. Měřen byl celkový čas, vytížení CPU.

Databázová struktura

Struktura tabulky na kterou je aplikován partitioning je zcela primitivní - má jediný sloupec, dle kterého je partitioning prováděn.

CREATE TABLE table_base (
    id         INTEGER PRIMARY KEY
);

K této tabulce je vytvořen požadovaný počet partitions, dělících interval [1,500000] na stejně velké díly. Vytvoření partition vypadá například takto:

CREATE TABLE table_100000 (
    PRIMARY KEY (id),
    CHECK (id > 0 AND id <= 100000)
) INHERITS (table_base);

Po vytvoření partitions jsou vytvořeny rules nebo trigger který samotný partitioning provádí. O tom ale podrobněji v následujících odstavcích, věnovaným jednotlivým způsobům způsobům partitioningu.

Trigger s dynamickými SQL dotazy

První možností je partitioning pomocí triggeru využívajícího dynamické SQL.

CREATE OR REPLACE FUNCTION partition_trigger() RETURNS trigger AS $$
DECLARE

  v_size INTEGER := 100000;
  v_id   INTEGER;

BEGIN

  v_id := ((NEW.id - 1) / v_size + 1) * v_size;

  EXECUTE 'INSERT INTO table_' || v_id || ' VALUES ($1)' USING NEW.id;

  RETURN NULL;

END;
$$ LANGUAGE plpgsql;

Ten na základě informace o velikosti partition určí partition do které má být záznam vložen, a pomocí dynamického SQL ho do ní přesměruje.

počet partitions celkový čas (s) vytížení CPU (%)
5 77 97
10 77 98
20 78 98

Srovnání s dalšími způsoby partitioningu nechme na později, ale je vidět že náročnost se v závislosti na počtu partitions v podstate nemění. Skript pro tento benchmark si můžete stáhnout zde.

Trigger bez dynamických SQL dotazů

Druhá možnost je partitioning pomocí triggeru bez využití dynamického SQL, ve kterém je pro každou partition uveden explicitní INSERT. Takový trigger může vypadat například takto:

CREATE OR REPLACE FUNCTION partition_trigger() RETURNS trigger AS $$
BEGIN

    IF (NEW.id > 0 AND NEW.id <= 100000) THEN
        INSERT INTO table_100000 VALUES (NEW.*);
    ELSIF (NEW.id > 100000 AND NEW.id <= 200000) THEN
        INSERT INTO table_200000 VALUES (NEW.*);
    ELSIF (NEW.id > 200000 AND NEW.id <= 300000) THEN
        INSERT INTO table_300000 VALUES (NEW.*);
    ELSIF (NEW.id > 300000 AND NEW.id <= 400000) THEN
        INSERT INTO table_400000 VALUES (NEW.*);
    ELSIF (NEW.id > 400000 AND NEW.id <= 500000) THEN
        INSERT INTO table_500000 VALUES (NEW.*);
    END IF;

    RETURN NULL;

END;
$$ LANGUAGE plpgsql;

Tento skript na rozdíl od předchozího způsobu nemusí spouštět žádné dynamické SQL příkazy, což by mělo zlepšit výkon - podívejme se tedy na výsledky

počet partitions celkový čas (s) vytížení CPU (%)
5 53 97
10 57 98
20 67 98

Je vidět že tentokrát náročnost v závislosti na počtu partitions roste - to je způsobeno rostoucím počtem IF podmínek v těle triggeru. Skript pro tento benchmark si můžete stáhnout zde.

Rules

Poslední možností jak provádět partitioning jsou rules, které jsou namísto triggerů (tj. vlastně procedur) založeny na přepisu execution planů. Pro každou partition je následujícím způsobem vytvořeno samostatné pravidlo:

CREATE OR REPLACE RULE rule_100000 AS ON INSERT TO table_base
          WHERE (id > 0 AND id <= 100000) DO INSTEAD
          INSERT INTO table_100000 VALUES (NEW.*);

Tento skript na rozdíl od předchozích způsobů (založených na triggerech) nemusí spouštět žádné procedury, ale plán dotazu je přepsán ještě ve fázi plánování.

počet partitions celkový čas (s) vytížení CPU (%)
5 15 83
10 22 90
20 34 90

I zde je vidět že závislost náročnosti na počtu partitions roste - to je způsobeno rostoucím počtem pravidel která je nutno vyhodnotit (vždy se vyhodnocují všechna). Skript pro tento benchmark si můžete stáhnout zde.

Shrnutí

Z uvedených řešení je bezkonkurenčně nejrychlejší to pomocí rules - je zhruba 2x rychlejší než řešení založená na triggerech. Kromě kratšího času je také znatelně méně náročnější na CPU. S rules jsou ale spojeny některé problémy, například co se týká zneplatnění execution planu v případě přidání nové partition, a podobně.

Testy byly prováděny na PostgreSQL 8.5alpha2.

Odkazy

Komentáře

K tomuto článku zatím žádné komentáře neexistují (nebo čekají na schválení).

Nový komentář

Všechny komentáře podléhají schválení - mezi odesláním komentáře a jeho zobrazením na této stránce tedy může být prodleva. Vyplníte-li e-mailovou adresu, budete o schválení či neschválení komentáře informováni.

V titulku ani v textu nejsou povoleny HTML tagy - budou automaticky odstraněny. Odstavec ukončíte prázdným řádkem.

(nepovinné)