sql >> Database teknologi >  >> RDS >> PostgreSQL

En oversigt over cachelagring til PostgreSQL

De fleste OLTP-arbejdsbelastninger involverer tilfældig disk I/O-brug. Ved at vide, at diske (inklusive SSD) er langsommere i ydeevne end at bruge RAM, bruger databasesystemer caching til at øge ydeevnen. Caching handler om at gemme data i hukommelsen (RAM) for hurtigere adgang på et senere tidspunkt.

PostgreSQL bruger også caching af sine data i et rum kaldet shared_buffers. I denne blog vil vi udforske denne funktionalitet for at hjælpe dig med at øge ydeevnen.

Grundlæggende om PostgreSQL-cache

Før vi dykker dybere ned i begrebet caching, lad os få en opfriskning af det grundlæggende.

I PostgreSQL er data organiseret i form af sider af størrelse 8KB, og hver sådan side kan indeholde flere tuples (afhængigt af størrelsen på tuple). En forenklet repræsentation kunne være som nedenfor:

PostgreSQL cacherer følgende for at fremskynde dataadgang:

  • Data i tabeller
  • Indekser
  • Forespørgselsudførelsesplaner

Mens caching af forespørgselsudførelsesplanen fokuserer på at gemme CPU-cyklusser; caching for tabeldata og indeksdata er fokuseret for at spare kostbar disk I/O-drift.

PostgreSQL lader brugere definere, hvor meget hukommelse de vil reservere til at opbevare en sådan cache til data. Den relevante indstilling er shared_buffers i postgresql.conf-konfigurationsfilen. Den endelige værdi af shared_buffers definerer, hvor mange sider der kan cachelagres på ethvert tidspunkt.

Når en forespørgsel udføres, søger PostgreSQL efter siden på disken, som indeholder den relevante tuple, og skubber den i shared_buffers-cachen for lateral adgang. Næste gang den samme tuple (eller en hvilken som helst tuple på samme side) skal tilgås, kan PostgreSQL gemme disk IO ved at læse den i hukommelsen.

I ovenstående figur, Side-1 og Side-2 af en bestemt tabellen er blevet cachelagret. Hvis en brugerforespørgsel skal have adgang til tuples mellem Tuple-1 til Tuple-200, kan PostgreSQL selv hente den fra RAM.

Men hvis forespørgslen skal have adgang til Tuples 250 til 350, skal den udføre disk I/O for side 3 og side 4.  Enhver yderligere adgang til Tuple 201 til 400 vil blive hentet fra cachen og disk I/O vil ikke være nødvendig – hvilket gør forespørgslen hurtigere.

På et højt niveau følger PostgreSQL LRU (senest brugt) algoritme for at identificere de sider, der skal fjernes fra cachen. Med andre ord, en side, der kun er tilgået én gang, har større chancer for udsættelse (sammenlignet med en side, der tilgås flere gange), i tilfælde af at en ny side skal hentes af PostgreSQL til cachen.

PostgreSQL-cache i aktion

Lad os udføre et eksempel og se effekten af ​​cache på ydeevnen.

Start PostgreSQL og hold shared_buffer indstillet til standard 128 MB

$ initdb -D ${HOME}/data

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

$ pg_ctl -D ${HOME}/data start

Opret forbindelse til serveren og opret en dummy-tabel tblDummy og et indeks på 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 );

Udfyld dummy-data med 200.000 tupler, sådan at der er 10.000 unikke p_id og for hver p_id er der 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 $$;

Genstart serveren for at rydde cachen. Udfør nu en forespørgsel og kontroller, hvor lang tid det tager at udføre den samme

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

Tjek derefter de blokke, der er læst fra disken

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

heap_blks_read | heap_blks_hit

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

10000          |             0

I ovenstående eksempel blev der læst 1000 blokke fra disken for at finde tæller, hvor c_id =1. Det tog 160 ms, siden der var disk I/O involveret at hente disse poster fra disken.

Udførelsen er hurtigere, hvis den samme forespørgsel udføres igen, da alle blokkene stadig er i cachen på PostgreSQL-serveren på dette stadium

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

og blokerer læst fra disken vs fra cache

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

heap_blks_read | heap_blks_hit

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

    0          |         10000

Det er tydeligt ovenfra, at da alle blokke blev læst fra cachen og ingen disk I/O var påkrævet. Dette gav derfor også resultaterne hurtigere.

Indstilling af størrelsen på PostgreSQL-cachen

Størrelsen af ​​cachen skal indstilles i et produktionsmiljø i overensstemmelse med mængden af ​​tilgængelig RAM samt de forespørgsler, der kræves for at blive udført.

Som et eksempel – shared_buffer på 128 MB er muligvis ikke tilstrækkelig til at cache alle data, hvis forespørgslen skulle hente flere tuples: 

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

Skift shared_buffer til 1024MB for at øge heap_blks_hit.

Faktisk, i betragtning af forespørgslerne (baseret på c_id), i tilfælde af at data reorganiseres, kan et bedre cache-hitforhold også opnås med en mindre shared_buffer.

I Data_Organization-1 skal PostgreSQL have 1000 bloklæsninger (og cacheforbrug) ) for at finde c_id=1. På den anden side, for Data_Organisation-2, for den samme forespørgsel, har PostgreSQL kun brug for 104 blokke.

Færre blokke, der kræves til den samme forespørgsel, bruger til sidst mindre cache og holder også forespørgselsudførelsestiden optimeret.

Konklusion

Mens shared_buffer vedligeholdes på PostgreSQL-procesniveau, tages kerneniveaucachen også i betragtning for at identificere optimerede forespørgselsudførelsesplaner. Jeg vil tage dette emne op i en senere serie af blogs.


  1. Hvordan vælger man kolonner fra en tabel, som ikke har nulværdier?

  2. SOUNDEX() Funktion i Oracle

  3. Sådan får du den aktuelle dato og tid (uden tidszone) i T-SQL

  4. Er GUID-kollisioner mulige?