Pg+dSpam: zvyseni vykonu

Tomas Vondra tv na fuzzy.cz
Středa Prosinec 14 16:36:43 CET 2011


On 14 Prosinec 2011, 14:57, Jan Kasprzak wrote:
> Pavel Kankovsky wrote:
> : On Wed, 14 Dec 2011, Jan Kasprzak wrote:
> :
> : > 	Ano, premyslel jsem o tom co by se stalo, kdybych to cele obalil
> : > do jedne transakce - nemel by to byt slozity patch.
> : > 	 Stalo by se to, ze by velmi pravdepodobne velmi brzo
> : > a casto dochazelo k deadlockum v pripade, kdy dve instance dspamu
> budou
> : > aktualizovat hit county u tech stejnych tokenu, ale sahnou na ne
> : > v opacnem poradi.
> :
> : Zdá se, že PostgreSQL nemá nic jako "MERGE" nebo "ON DUPLICATE KEY", co
> by
> : dovolilo v jednom kroku záznam buď vložit nebo aktualizovat.
>
> 	Tak hlavne Pg nema (donedavna nemel?) subtransakce, coz je muj hlavni
> problem co mi na Pg proti Oraclu vadi. Takze kdyz spadne INSERT (nebo
> obecne
> libovolny SQL prikaz), tak je cela transakce v haji a musi se odrolovat
> zpet.
> V Oraclu bych normalne otevrel transakci, udelal insert, pokud spadne
> tak update, a jel bych dal. Na konci bych commitnul. Samozrejme bych musel
> tokeny mit nejak setridene kvuli deadlocku, ale zase bych porad
> vykonaval ten stejny insert a ten stejny select s bindovanymi parametry,
> takze by byl v SQL cache, a bylo by to cele rychle.

V PostgreSQL jsou dvě cesty jak se s tímto vypořádat (o kterých vím) -
odchytit a ošetřit výjimky např. v PL/pgSQL a použít savepointy. Oboje si
ale musíte oprogramovat a není to zadarmo, to je fakt.

Ono to chování u Oracle je to dáno tím jak mají udělaný MVCC systém -
rollback znamená že musí skutečně odstranit změny z datových bloků (mají
redo/undo), takže nejdříve odrolují jenom ten spadlý dotaz a čekají jestli
uživatel odroluje i zbytek nebo bude pokračovat.

Oproti tomu u PostgreSQL nestojí rollback nic (žádné undo neexistuje).
Pokud chcete, můžete zaplatit za savepointy ale standardně se odroluje
všechno. To není bug, to je feature.

> : Ale možná by
> : šla použít tato finta
> :
> : INSERT INTO table SELECT values... WHERE
> : 	(SELECT COUNT(key) FROM table WHERE key = keyval) = 0
> :
> : a tím podmíněně vytvořit nový záznam s nulovými hodnotami a za něj
> : přidat normální UPDATE.
> :
> : Alternativně by možná šlo heuristicky zkusit nejdřív všechny INSERT-y
> : zkusit dávkově v jedné transakci a teprve v případě neúspěchu je
> provádět
> : individuálně. Hádám, že by pravděpodobnost výskytu situace, kdy se dva
> : procesy pokoušejí nově zaevidovat ten samý token, měla v čase velmi
> rychle
> : klesat.
>
> 	Jo. Nebo to v pripade neuspechu rozdelit na poloviny, pak na ctvrtiny,
> atd. :-).

Stačí jednoduchá funkce která vrátí true nebo false podle toho jestli se
insert povedl. Něco jako:

CREATE OR REPLACE PROCEDURE insert_token(p_token VARCHAR, p_pocet INT,
...) RETURN boolean
AS $$

   INSERT INTO spam_token (...) VALUES (...);
   RETURN true;

EXCEPTION
   WHEN others THEN
      RETURN false;

$$ LANGUAGE plpgsql;




Další informace o konferenci Linux