Pg+dSpam: zvyseni vykonu

Tomas Vondra tv na fuzzy.cz
Středa Prosinec 14 14:37:48 CET 2011


On 14 Prosinec 2011, 13:51, 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.
>
> Řekl bych, že je to celé o dost zajímavější, protože to vypadá, že
> podpora pro masovou změnu v rámci jedné transakce tam už je. Dívám se na
> zdroják driveru v GITu:
>
> <http://dspam.git.sourceforge.net/git/gitweb.cgi?p=dspam/dspam;a=blob;f=src/pgsql_drv.c;h=6746d23246eaef59b7b9ad3f1db626cefc512272;hb=HEAD>
>
> na funkci _ds_setall_spamrecords(). Ta dělá takovou zvláštní schizoidní
> věc: Někde asi tak na čtvrté nebo páté stránce cyklí přes obsah argumentu
> "diction", kde jsou zjevně tokeny, které má zpracovat a je-li potřeba
> čítač změnit, tak vyrobí příslušný příkaz UPDATE, který přidá do bufferu
> "update", kde k tomu na začátek přilepí "BEGIN" a na konec "COMMIT"
> a nakonec to pošle do db, čili všechny updaty provede v jedné transakci.
>
> Ale má to výjimku! Jestliže je to nový ten, který v db dosud není (a tudíž
> ho nelze měnit), tak sestaví INSERT a ten provede samostatně hned uvnitř
> smyčky (a pokud selže, tak to přidá do update).

Ale to není až tak schizoidní - naopak je to celkem rozumné řešení. Pokud
navíc ty příkazy UPDATE jsou vždy ve všech batchích stejně seřazené (třeba
podle ID) tak je to i odolné oproti deadlockům. Nemyslím že by se dalo
vymyslet něco lepšího ...

> Takže dávkové transakce tam už jsou a nějak se to s nimi musí umět
> vypořádat (možná je tam někde něco, co zajistí, že je seznam vždy
> setříděný stejným způsobem?), ale problém bych hledal v přidávání záznamů
> pro nové tokeny.

Přesně.

> 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. 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.

Ne, PostgreSQL bohužel MERGE (zatím) nemá. A ta vaše finta také fungovat
nebude, protože ten INSERT vidí READ COMMITTED snapshot z okamžiku
spuštění příkazu, takže pokud v době běhu nějaká další transakce commitne
vhodný záznam tak to stejně spadne.

> 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.

To asi moc dobře fungovat nebude, protože pak stačí aby spadnul jeden
INSERT z toho batche a spadne vám celý batch. Já osobně bych šel jinou
cestou - nedělat to pro každý token, ale někde bokem si nasyslit a vždycky
při dosažení stanovené hodnoty zkusit

SELECT token FROM tabulka WHERE token IN (token1, token2, ...)

čímž jedním dotazem zjistím které tokeny už tam jsou (tj. nemá cenu pro ně
dělat INSERT - šup s nimi rovnou do UPDATE batche), a pak zkusit INSERT
jenom na ty zbývající. Případně ještě použít zámek (advisory lock), tak
aby ty INSERTY skutečně nepadaly.

A ještě jeden nápad - používá se na té velké tabulce HOT update? To může
databázi hodně ulehčit, tabulka a indexy nebobtnají apod. Pro zjištění
stačí dotaz:

  SELECT schemaname, relname,
         n_tup_upd,n_tup_hot_upd,
         case when n_tup_upd > 0
            then
((n_tup_hot_upd::numeric/n_tup_upd::numeric)*100.0)::numeric(5,2)
            else NULL
         end AS hot_ratio
   FROM pg_stat_all_tables WHERE relname = 'tabulka';

Čím vyšší hot_ratio, tím lepší (maximum je 100%). Problém je pokud v
blocích není místo (to jde řešit snížením fillfactoru), nebo pokud se
updatují indexované sloupce (to řešit nejde, leda úpravou dotazů).

Tomáš



Další informace o konferenci Linux