sql >> Databáze >  >> RDS >> PostgreSQL

Přehled ukládání do mezipaměti pro PostgreSQL

Většina úloh OLTP zahrnuje náhodné použití I/O disku. Databázové systémy s vědomím, že disky (včetně SSD) jsou výkonově pomalejší než používání RAM, používají ke zvýšení výkonu ukládání do mezipaměti. Ukládání do mezipaměti je o ukládání dat do paměti (RAM) pro rychlejší přístup v pozdějším okamžiku.

PostgreSQL také využívá ukládání svých dat do mezipaměti v prostoru zvaném shared_buffers. V tomto blogu prozkoumáme tuto funkci, abychom vám pomohli zvýšit výkon.

Základy ukládání do mezipaměti PostgreSQL

Než se hlouběji ponoříme do konceptu ukládání do mezipaměti, pojďme si trochu oprášit základy.

V PostgreSQL jsou data organizována ve formě stránek o velikosti 8KB a každá taková stránka může obsahovat více n-tic (v závislosti na velikosti n-tic). Zjednodušená reprezentace by mohla vypadat následovně:

PostgreSQL ukládá do mezipaměti následující údaje pro urychlení přístupu k datům:

  • Data v tabulkách
  • Indexy
  • Plány provádění dotazů

Zatímco se cachování plánu provádění dotazů zaměřuje na ukládání cyklů CPU; ukládání do mezipaměti pro data tabulky a data indexu je zaměřeno na úsporu nákladných diskových I/O operací.

PostgreSQL umožňuje uživatelům definovat, kolik paměti by chtěli vyhradit pro uchování takové mezipaměti pro data. Relevantní nastavení je shared_buffers v konfiguračním souboru postgresql.conf. Konečná hodnota shared_buffers definuje, kolik stránek lze v kterémkoli okamžiku uložit do mezipaměti.

Jakmile se provádí dotaz, PostgreSQL hledá stránku na disku, která obsahuje příslušnou n-tici, a vkládá ji do mezipaměti shared_buffers pro laterální přístup. Až bude příště potřeba přistupovat ke stejné n-tice (nebo jakékoli n-tice na stejné stránce), PostgreSQL může uložit IO disku tím, že jej přečte v paměti.

Na výše uvedeném obrázku jsou stránky 1 a 2 určitého tabulka byla uložena do mezipaměti. V případě, že uživatelský dotaz potřebuje získat přístup k n-ticím mezi Tuple-1 až Tuple-200, PostgreSQL je může načíst ze samotné RAM.

Pokud však dotaz potřebuje přístup k n-tice 250 až 350, bude muset provést diskový vstup/výstup pro stránku 3 a stránku 4.  Jakýkoli další přístup pro n-tice 201 až 400 bude načten z mezipaměti a diskový vstup/výstup nebude potřeba – tím bude dotaz rychlejší.

Na vysoké úrovni se PostgreSQL řídí algoritmem LRU (nejméně nedávno používaný) identifikovat stránky, které je třeba z mezipaměti odstranit. Jinými slovy, stránka, na kterou se vstoupí pouze jednou, má vyšší šanci na vystěhování (ve srovnání se stránkou, která je navštívena vícekrát), v případě, že je potřeba novou stránku načíst PostgreSQL do mezipaměti.

Ukládání do mezipaměti PostgreSQL v akci

Uvedeme příklad a uvidíme vliv mezipaměti na výkon.

Spustit PostgreSQL s nastavením sdílené vyrovnávací paměti na výchozí 128 MB

$ initdb -D ${HOME}/data

$ echo “shared_buffers=128MB” >> ${HOME}/data/postgresql.conf

$ pg_ctl -D ${HOME}/data start

Připojte se k serveru a vytvořte fiktivní tabulku tblDummy a index na c_id

CREATE Table tblDummy

(

id serial primary key,

p_id int,

c_id int,

entry_time timestamp,

entry_value int,

description varchar(50)  

);

CREATE INDEX ON tblDummy(c_id );

Naplňte fiktivní data 200 000 n-ticemi, takže existuje 10 000 jedinečných p_id a na každé p_id připadá 200 c_id 

DO $$

DECLARE

random_value integer:= 1;

BEGIN

FOR p_id_ctr IN 1..10000 BY 1 LOOP               

FOR c_id_ctr IN 1..200 BY 1 LOOP                                 

random_value = (( random() * 75 ) + 25);

INSERT INTO tblDummy (p_id,c_id,entry_time, entry_value, description )

VALUES (p_id_ctr,c_id_ctr,'now', random_value, CONCAT('Description for :',p_id_ctr, c_id_ctr));

END LOOP ;

END LOOP ;                      

END $$;

Restartujte server a vymažte mezipaměť. Nyní proveďte dotaz a zkontrolujte, jak dlouho trvá jeho provedení

SELECT pg_stat_reset();

EXPLAIN ANAYZE SELECT count(*) from tbldummy where c_id=1;



                           QUERY PLAN

--------------------------------------------------------------

 Aggregate  (cost=17407.33..17407.34 rows=1 width=8) (actual time=160.269..160.269 rows=1 loops=1)

   ->  Bitmap Heap Scan on tbldummy  (cost=189.52..17382.46 rows=9948 width=0) (actual time=10.627..156.275 rows=10000 loops=1)

         Recheck Cond: (c_id = 1)

         Heap Blocks: exact=10000

         ->  Bitmap Index Scan on tbldummy_c_id_idx  (cost=0.00..187.04 rows=9948 width=0) (actual time=5.091..5.091 rows=10000 loops=1)

               Index Cond: (c_id = 1)

 Planning Time: 1.325 ms

 Execution Time: 160.505 ms

Potom zkontrolujte bloky načtené z disku

SELECT heap_blks_read, heap_blks_hit from pg_statio_user_tables where relname='tbldummy';

heap_blks_read | heap_blks_hit

---------------+---------------

10000          |             0

Ve výše uvedeném příkladu bylo z disku načteno 1 000 bloků, aby se našly početní n-tice, kde c_id =1. Načtení těchto záznamů z disku trvalo 160 ms, protože se disk I/O účastnil.

Provedení je rychlejší, pokud je stejný dotaz znovu spuštěn, protože všechny bloky jsou v této fázi stále v mezipaměti serveru PostgreSQL

SELECT pg_stat_reset();

EXPLAIN ANAYZE SELECT count(*) from tbldummy where c_id=1;

                                                               

                                 QUERY PLAN

-------------------------------------------------------------------------------------

 Aggregate  (cost=17407.33..17407.34 rows=1 width=8) (actual time=33.760..33.761 rows=1 loops=1)

   ->  Bitmap Heap Scan on tbldummy  (cost=189.52..17382.46 rows=9948 width=0) (actual time=9.584..30.576 rows=10000 loops=1)

         Recheck Cond: (c_id = 1)

         Heap Blocks: exact=10000

         ->  Bitmap Index Scan on tbldummy_c_id_idx  (cost=0.00..187.04 rows=9948 width=0) (actual time=4.314..4.314 rows=10000 loops=1)

               Index Cond: (c_id = 1)

 Planning Time: 0.106 ms

 Execution Time: 33.990 ms

a blokuje čtení z disku vs z mezipaměti

SELECT heap_blks_read, heap_blks_hit from pg_statio_user_tables where relname='tbldummy';

heap_blks_read | heap_blks_hit

---------------+---------------

    0          |         10000

Z výše uvedeného je patrné, že jelikož byly všechny bloky načteny z mezipaměti a nebyl vyžadován žádný diskový vstup/výstup. Díky tomu byly výsledky také rychlejší.

Nastavení velikosti mezipaměti PostgreSQL

Velikost mezipaměti je třeba vyladit v produkčním prostředí v souladu s množstvím dostupné paměti RAM a také s požadovanými dotazy ke spuštění.

Jako příklad – sdílený_buffer o velikosti 128 MB nemusí stačit k mezipaměti všech dat, pokud měl dotaz načíst více n-tic: 

SELECT pg_stat_reset();

SELECT count(*) from tbldummy where c_id < 150;

SELECT heap_blks_read, heap_blks_hit from pg_statio_user_tables where relname='tbldummy';

 heap_blks_read | heap_blks_hit

----------------+---------------

      20331     |      288

Změňte sdílený_buffer na 1024 MB, abyste zvýšili heap_blks_hit.

Ve skutečnosti, vezmeme-li v úvahu dotazy (na základě c_id), v případě reorganizace dat lze dosáhnout lepšího poměru zásahů do mezipaměti také s menší sdílenou vyrovnávací pamětí.

V Data_Organization-1 bude PostgreSQL potřebovat 1000 čtení bloků (a spotřebu mezipaměti ) pro nalezení c_id=1. Na druhou stranu pro Data_Organisation-2 bude pro stejný dotaz PostgreSQL potřebovat pouze 104 bloků.

Méně bloků požadovaných pro stejný dotaz nakonec spotřebuje méně mezipaměti a také optimalizuje dobu provádění dotazu.

Závěr

Zatímco shared_buffer je udržován na úrovni procesu PostgreSQL, cache na úrovni jádra je také brána v úvahu pro identifikaci optimalizovaných plánů provádění dotazů. Tomuto tématu se budu věnovat v pozdější sérii blogů.


  1. Nástroje pro správu SQL Server 2017

  2. Zlepšete výkon dotazů SQL Server na velkých tabulkách

  3. SQL LIKE podmínka pro kontrolu celého čísla?

  4. Migrace databáze Microsoft Access na SQL Server