Výsledky benchmarku dle velikosti bloků na HDD - úvod
Zhruba před dvěma týdny jsem se zmiňoval o pokusu zjistit jaký dopad bude mít na PostgreSQL změna velikosti bloku - jak na úrovni souborového systému tak databáze. V té době už jsem měl k dispozici první výsledky, ale několik lidí poukázalo na ne zcela reprezentativní konfiguraci (zejména shared_buffers a checkpoint_segments). Nakonec jsem se rozhodl benchmark provést znovu s upravenou konfigurací a ještě jsem ho trochu rozšířil - takže nakonec místo týdne běžel dva. Dnes doběhl (hurá!), takže se pojďme podívat na výsledky.
Vygenerované výsledky benchmarku jsou dostupné zde - v této první části se pokusím stručně vysvětlit jak s nimi pracovat (co znamenají, jak je číst a srovnávat). Podíváme se také na několik ne zcela zřejmých úskalí konfigurace která je třeba mít na paměti při srovnávání výsledků.
Na výsledky OLTP a TPC-H částí se budu blíže zabývat v následujících dvou samostatných postech.
Jak už jsem uvedl, výsledky benchmarku (zatím jenom z HDD), jsou dostupné zde. Hned na úvodní stránce je vypsán seznam 24 souborových systémů které byly součástí benchmarku

Kliknutím na libovolný ze souborových systémů zobrazíte jeho výsledky, ale můžete také několik souborových systémů srovnat - stačí u vybraných položek zaškrtnout checkbox a kliknout na "compare" pod tabulkou. Případně můžete výsledky vybraných souborových systémů uložit do CSV.
Dva typy obrázků
Výsledky jednotlivých systémů jsou znázorňovány pomocí dvou typů obrázků. Prvním typem jsou jednoduché grafy jako je např. tento:

Na ose "x" jsou uvedeny velikosti bloku PostgreSQL (1kB až 32kB), na ose "y" jsou uvedeny velikosti bloku souborového systému. V jednotlivých buňkách jsou uvedeny hodnoty které bylo s daným souborovým systémem a kombinací velikosti bloků dosaženo - např. z výše uvedeného obrázku je možné vyčíst že pro "ext3" (v plně žurnálovacím módu a se zapnutými write bariérami)" bylo pro 8kB bloky databáze a 2kB bloky filesystému dosaženo 261.87 tps.
Obrázek je pro názornost obarven (na pravé straně je uvedena škála) - v případě porovnávání několika souborových systémů (pomocí tlačítka "compare") je škála společná aby bylo srovnání jednodušší. Jinak je škála určena samostatně pro každý obrázek.
Druhý typ obrázků ilustruje průběh pgbench testu - tps a latenci během 10-minutových běhů. Například tento obrázek znázorňuje hodnotu tps pro read-write test pro souborový systém xfs (se zapnutými write bariérami) pro 8kB bloky databáze a 4kB bloky souborového systému. Červená barva je použita pro okamžitou hodnotu, modrá průměr pro každých 10 vteřin.

Modrá oblast určuje dobu kdy běžel checkpoint (modrá znamená "timed" checkpoint, červená "xlog" checkpoint). Z obrázku je zřejmé že ve 4:00 byl spuštěn standardní "timed" checkpoint který běžel do 8:00, kdy byl spuštěn další "timed" checkpoint - což je očekávané chování, protože interval checkpointu byl nastaven na 4 minuty a completion target na 0.9.
Doplňující informací je latence, znázorněná na tomto obrázku (stejný souborový systém ale read-only test). Na ose "x" je opět čas testu, na ose "y" hodnota latence.

Vzhledem k tomu že většina transakcí má nízkou latenci, několik dlouhých transakcí snižuje čitelnost grafu v nízkých hodnotách - proto je k dispozici i log-scale graf latence (kdy osa "y" je logaritmická).

Z grafu je mimo jiné pěkně vidět které transakce byly vyřízeny z cache (latence pod 1 ms) a které vyžadovaly přístup na disk. I v grafech latence jsou znázorňovány checkpointy, nicméně v tomto případě nemají příliš co čistit protože se jedná o read-only test.
Hodnoty tps a latenci pro různé velikosti bloků samozřejmě nelze dostat do jednoho obrázku místo toho jsou grafy vypsány vedle sebe do tabulky, tak aby směrem dolů rostla velikost databázového bloku (1kB, 2kB, ... až 32kB) a směrem v pravo velikost bloku souborového systému (závisí na jednotlivých souborových systémech, většinou 1kB až 4kB).

Doufám že tento popis grafů stačí - pokud narazíte na nejasnost, ozvěte se.
Problémy
Čímž se dostáváme k druhému bodu - konfiguračním problémům na které narazíte pokud změníte velikost databázového bloku, zejména pokud se budete snažit výsledky porovnávat. V konfiguraci PostgreSQL je totiž několik položek které se velikosti bloku úzce týkají a přitom není jasné jak je po změně velikosti bloku změnit.
random_page_cost, seq_page_cost vs. velikost bloku
První hodnotou jsou samozřejmě ceny - zejména seq_page_cost a random_page_cost. Uvažujme tabulku obsahující cca 100MB dat, což při standardní velikosti bloku 8kB znamená cca 13000 bloků. Pokud velikost db bloku změníme na 32kB, rázem počet bloků poklesne na cca 3300. S počtem bloků samozřejmě klesá i odhad ceny sekvenčního skenu - přitom ale množství dat je stále stejné.
Ano, ceny dotazů jsou bezrozměrné a primární je poměr cen různých plánů - např. sekvenčního skenu vs. index skenu. Cena index skenu ale zřejmě neklesá s velikostí bloku tak rychle, takže se může stát např. že ačkoliv se množství dat nezmění, pro velké bloky chybně vyhraje sekvenční sken zatímco pro malé bloky chybně vyhraje index scan.
Na dotazy v pgbech části to nemá žádný vliv (vesměs používají indexy bez ohledu na velikost bloku), důležitou roli to ale hraje v TPC-H části kde byla volba správného plánu zcela zásadní (pro některé velikosti bloků některé dotazy ani nedoběhnou).
Nenašel jsem příčetně vypadající algoritmus jak upravovat ceny podle velikosti bloku, takže jsem pro všechny běhy nechal stejné hodnoty - pro HDD implicitní, pro SSD jsem snížil cenu random_page_cost na 2. Máte-li návrh jak proměnné upravit, dejte mi vědět.
background writer
Dalším kamenem úrazu je background writer, který průběžně zapisuje špinavé bloky na disk. Jeho aktivita je totiž ovlivňována proměnnou bgwriter_lru_maxpages která určuje kolik bloků se má při každém běhu zapsat na disk. Implicitní hodnota je 100, což ale při velikosti bloku 1kB znamená 100kB, zatímco při 32kB blocích to je 3,2MB.
Důsledkem je nepoměr v množství dat zapsaných během read-write pgbench testu, znázorněný na následujícím obrázku (počítají se jen buffery):

tj. zatímco pro 1kB bloky se zapíše sotva 80MB, pro 32kB bloky se zapsalo více než 1.5GB! Když si to rozložíme na jednotlivé složky - kolik se zapsalo během checkpointu a kolik díky bgwriteru, vypadá to takto (nejdříve checkpoint):

a nyní vlastní background writer

Tj. hlavní objem dat má skutečně má na starosti background writer, ačkoliv i u checkpointů je zřejmý nepoměr mezi malými a velkými bloky.
Mohlo by se zdát že stačí hodnotu jednoduše násobit / dělit tak aby byl zachován konstantní objem dat, ale stejně jako u cost proměnných to není tak jednoduché. Velké bloky dat se totiž rychleji špiní a db cache je "menší" (co se počtu bufferů týká) takže je třeba ji během read-write testu častěji čistit, takže agresivnější background writer v podstatě dává smysl.
Stejně jako u cost proměnných platí že máte-li nápad jak konfiguraci background writeru upravit, budu rád dáte-li mi mi vědět.
checkpointing
A na závěr drobná poznámka ohledně spolehlivosti hodnot u checkpointingu (počtu zapsaných bufferů apod.). Checkpoint se bohužel aktuálně chová tak že do logu (a do pg_stat_bgwriter) jsou data zapsána až po dokončení, tj. pokud checkpoint běžel v okamžiku skončení pgbench běhu, není tento checkpoint do výsledků bohužel zahrnut.
V principu by nepomohlo ani kdybych po skončení benchmarku spustil vynucený checkpoint - nedokázal bych totiž rozlišit kolik bufferů se zapsalo během benchmarku a kolik až po jeho skončení. Do aktuálního commitfestu jsem přihlásil drobný patch díky kterému se do logu budou zapísovat alespoň základní průběžné informace o průběhu checkpointu.
To je na úvod vše - v příštím postu už se budu věnovat výsledkům pgbench části.




