Besparje in penny op grutte folumes yn PostgreSQL

Trochgean mei it ûnderwerp fan it opnimmen fan grutte gegevensstreamen opwekt troch foarige artikel oer partitioning, yn dit wy sille sjen nei de manieren wêrop jo kinne ferminderje de "fysike" grutte fan de opslein yn PostgreSQL, en har ynfloed op serverprestaasjes.

Wy sille prate oer TOAST ynstellings en gegevens alignment. "Gemiddeld" sille dizze metoaden net te folle boarnen besparje, mar sûnder de applikaasjekoade hielendal te wizigjen.

Besparje in penny op grutte folumes yn PostgreSQL
Lykwols, ús ûnderfining die bliken te wêzen hiel produktyf yn dit ferbân, sûnt de opslach fan hast alle tafersjoch troch syn aard is meastentiids taheakje-allinne yn termen fan opnommen gegevens. En as jo jo ôffreegje hoe't jo de databank kinne leare om ynstee op skiif te skriuwen 200MB / s heal safolle - asjebleaft ûnder kat.

Lytse geheimen fan grutte gegevens

By baan profyl ús tsjinst, se fleane geregeld nei him út 'e laarzen tekstpakketten.

En sûnt VLSI komplekswaans databank wy kontrolearje is in multi-komponint produkt mei komplekse gegevens struktueren, dan queries foar maksimale prestaasjes lykje krekt sa "multi-folume" mei komplekse algoritmyske logika. Dat it folume fan elke yndividuele eksimplaar fan in oanfraach of it resultearjende útfieringsplan yn it log dat nei ús komt, blykt "gemiddeld" frij grut te wêzen.

Litte wy nei de struktuer sjen fan ien fan 'e tabellen wêryn't wy "rûge" gegevens skriuwe - dat is, hjir is de orizjinele tekst fan 'e logyngong:

CREATE TABLE rawdata_orig(
  pack -- PK
    uuid NOT NULL
, recno -- PK
    smallint NOT NULL
, dt -- ключ секции
    date
, data -- самое главное
    text
, PRIMARY KEY(pack, recno)
);

In typysk teken (al yndield, fansels, dus dit is in seksje-sjabloan), wêrby't it wichtichste is de tekst. Soms frij voluminous.

Tink derom dat de "fysike" grutte fan ien rekord yn in PG net mear dan ien side mei gegevens kin besette, mar de "logyske" grutte is in folslein oare saak. Om in volumetryske wearde (varchar/text/bytea) nei in fjild te skriuwen, brûk TOAST technology:

PostgreSQL brûkt in fêste sidegrutte (typysk 8 KB), en lit tuples net meardere siden oerspanje. Dêrom is it ûnmooglik om direkt opslaan hiel grutte fjild wearden. Om dizze beheining te oerwinnen, wurde grutte fjildwearden komprimearre en / of ferdield oer meardere fysike rigels. Dit bart ûngemurken troch de brûker en hat net folle ynfloed op de measte serverkoade. Dizze metoade is bekend as TOAST ...

Yn feite, foar elke tafel mei "potinsjeel grutte" fjilden, automatysk in paired tabel mei "slicing" wurdt makke elk "grutte" rekord yn 2KB segminten:

TOAST(
  chunk_id
    integer
, chunk_seq
    integer
, chunk_data
    bytea
, PRIMARY KEY(chunk_id, chunk_seq)
);

Dat is, as wy in tekenrige moatte skriuwe mei in "grutte" wearde data, dan sil de echte opname plakfine net allinnich oan 'e wichtichste tafel en syn PK, mar ek nei TOAST en syn PK.

It ferminderjen fan TOAST ynfloed

Mar de measte fan ús records binne noch net sa grut, moat passe yn 8KB - Hoe kin ik hjir jild op besparje?

Dit is wêr't it attribút ús helpt STORAGE by de tabel kolom:

  • UTLENGE makket sawol kompresje as aparte opslach mooglik. Dit standert opsje foar de measte TOAST-kompatibele gegevenstypen. It besiket earst kompresje út te fieren, dan bewarret it bûten de tafel as de rige noch te grut is.
  • FOARNAAMSTE lit kompresje mar net aparte opslach. (Yn feite sil aparte opslach noch wurde útfierd foar sokke kolommen, mar allinich as lêste ynstânsje, as d'r gjin oare manier is om de tekenrige te krimpen sadat it op de side past.)

Yn feite is dit krekt wat wy nedich binne foar de tekst - compress it safolle mooglik, en as it net past hielendal, set it yn TOAST. Dit kin direkt op 'e flecht dien wurde, mei ien kommando:

ALTER TABLE rawdata_orig ALTER COLUMN data SET STORAGE MAIN;

Hoe te evaluearjen it effekt

Sûnt de gegevensstream feroaret elke dei, kinne wy ​​​​net absolute nûmers fergelykje, mar yn relative termen lytser oandiel Wy hawwe it opskreaun yn TOAST - safolle te better. Mar d'r is hjir in gefaar - hoe grutter it "fysike" folume fan elke yndividuele rekord, hoe "breder" de yndeks wurdt, om't wy mear siden mei gegevens moatte dekke.

Ôfdieling foardat feroarings:

heap  = 37GB (39%)
TOAST = 54GB (57%)
PK    =  4GB ( 4%)

Ôfdieling nei feroarings:

heap  = 37GB (67%)
TOAST = 16GB (29%)
PK    =  2GB ( 4%)

Yn feite, wy begûn te skriuwen oan TOAST 2 kear minder faak, dy't net allinich de skiif, mar ek de CPU ûntladen:

Besparje in penny op grutte folumes yn PostgreSQL
Besparje in penny op grutte folumes yn PostgreSQL
Ik sil opmerke dat wy ek lytser wurden binne yn it "lêzen" fan 'e skiif, net allinich "skriuwen" - om't by it ynfoegjen fan in record yn in tabel, wy ek in diel fan 'e beam fan elke yndeks moatte "lêze" om har te bepalen takomstige posysje yn harren.

Wa kin goed libje op PostgreSQL 11

Nei it bywurkjen fan PG11, hawwe wy besletten om troch te gean mei "tuning" TOAST en merkten op dat fanôf dizze ferzje de parameter toast_tuple_target:

De TOAST-ferwurkingskoade wurdt allinich fjoer as de rigewearde dy't yn 'e tabel opslein wurde moat grutter is dan TOAST_TUPLE_THRESHOLD bytes (meastentiids 2 KB). De TOAST-koade sil fjildwearden komprimearje en / of ferpleatse út 'e tabel oant de rigewearde minder wurdt dan TOAST_TUPLE_TARGET bytes (fariabele wearde, ek normaal 2 KB) of de grutte kin net wurde fermindere.

Wy besletten dat de gegevens dy't wy normaal hawwe "hiel koart" of "hiel lang" binne, dus besleaten wy ússels te beheinen ta de minimaal mooglike wearde:

ALTER TABLE rawplan_orig SET (toast_tuple_target = 128);

Litte wy sjen hoe't de nije ynstellings de skiifladen beynfloede hawwe nei rekonfiguraasje:

Besparje in penny op grutte folumes yn PostgreSQL
Net min! Trochsneed de wachtrige nei de skiif is ôfnommen likernôch 1.5 kear, en de skiif "drokke" is 20 prosint! Mar miskien dit op ien of oare manier beynfloede de CPU?

Besparje in penny op grutte folumes yn PostgreSQL
Minder waard it teminsten net. Hoewol, it is lestich om te oardieljen as sels sokke folumes de gemiddelde CPU-lading noch net heger kinne ferheegje 5%.

Troch de plakken fan 'e termen te feroarjen feroaret de som ...!

Lykas jo witte, besparret in penny in roebel, en mei ús opslachvoluminten giet it oer 10TB / moanne sels in bytsje optimalisaasje kin jaan in goede winst. Dêrom hawwe wy omtinken jûn oan de fysike struktuer fan ús gegevens - hoe krekt "steapele" fjilden binnen it rekord elk fan 'e tabellen.

Want fanwegen data alignment dit is rjocht foarút beynfloedet de resultearjende folume:

In protte arsjitektuer jouwe gegevensôfstimming op grinzen fan masinewurden. Bygelyks, op in 32-bit x86 systeem, hiele getallen (integer type, 4 bytes) wurde ôfstimd op in 4-byte wurd grins, lykas dûbele presys driuwende punt nûmers (dûbele presyzje driuwende punt, 8 bytes). En op in 64-bit systeem sille dûbele wearden wurde ôfstimd op 8-byte wurdgrinzen. Dit is in oare reden foar ynkompatibiliteit.

Troch ôfstimming hinget de grutte fan in tabelrige ôf fan 'e folchoarder fan 'e fjilden. Meastentiids is dit effekt net heul merkber, mar yn guon gefallen kin it liede ta in signifikante ferheging fan grutte. As jo ​​​​bygelyks char (1) en integerfjilden mingje, sille d'r typysk 3 bytes fergriemd wurde tusken har.

Litte wy begjinne mei syntetyske modellen:

SELECT pg_column_size(ROW(
  '0000-0000-0000-0000-0000-0000-0000-0000'::uuid
, 0::smallint
, '2019-01-01'::date
));
-- 48 байт

SELECT pg_column_size(ROW(
  '2019-01-01'::date
, '0000-0000-0000-0000-0000-0000-0000-0000'::uuid
, 0::smallint
));
-- 46 байт

Wêr kamen in pear ekstra bytes wei yn it earste gefal? It is ienfâldich - 2-byte smallint ôfstimd op 4-byte grins foar de folgjende fjild, en as it is de lêste, der is neat en gjin needsaak om align.

Yn teory is alles goed en kinne jo de fjilden opnij regelje as jo wolle. Litte wy it kontrolearje op echte gegevens mei it foarbyld fan ien fan 'e tabellen, wêrfan de deistige seksje 10-15GB beslacht.

Inisjele struktuer:

CREATE TABLE public.plan_20190220
(
-- Унаследована from table plan:  pack uuid NOT NULL,
-- Унаследована from table plan:  recno smallint NOT NULL,
-- Унаследована from table plan:  host uuid,
-- Унаследована from table plan:  ts timestamp with time zone,
-- Унаследована from table plan:  exectime numeric(32,3),
-- Унаследована from table plan:  duration numeric(32,3),
-- Унаследована from table plan:  bufint bigint,
-- Унаследована from table plan:  bufmem bigint,
-- Унаследована from table plan:  bufdsk bigint,
-- Унаследована from table plan:  apn uuid,
-- Унаследована from table plan:  ptr uuid,
-- Унаследована from table plan:  dt date,
  CONSTRAINT plan_20190220_pkey PRIMARY KEY (pack, recno),
  CONSTRAINT chck_ptr CHECK (ptr IS NOT NULL),
  CONSTRAINT plan_20190220_dt_check CHECK (dt = '2019-02-20'::date)
)
INHERITS (public.plan)

Seksje nei it feroarjen fan kolom folchoarder - krekt deselde fjilden, krekt oare folchoarder:

CREATE TABLE public.plan_20190221
(
-- Унаследована from table plan:  dt date NOT NULL,
-- Унаследована from table plan:  ts timestamp with time zone,
-- Унаследована from table plan:  pack uuid NOT NULL,
-- Унаследована from table plan:  recno smallint NOT NULL,
-- Унаследована from table plan:  host uuid,
-- Унаследована from table plan:  apn uuid,
-- Унаследована from table plan:  ptr uuid,
-- Унаследована from table plan:  bufint bigint,
-- Унаследована from table plan:  bufmem bigint,
-- Унаследована from table plan:  bufdsk bigint,
-- Унаследована from table plan:  exectime numeric(32,3),
-- Унаследована from table plan:  duration numeric(32,3),
  CONSTRAINT plan_20190221_pkey PRIMARY KEY (pack, recno),
  CONSTRAINT chck_ptr CHECK (ptr IS NOT NULL),
  CONSTRAINT plan_20190221_dt_check CHECK (dt = '2019-02-21'::date)
)
INHERITS (public.plan)

It totale folume fan 'e seksje wurdt bepaald troch it oantal "feiten" en hinget allinich ôf fan eksterne prosessen, dus litte wy de grutte fan 'e heap diele (pg_relation_size) troch it oantal records dêryn - dat is, wy krije gemiddelde grutte fan werklike bewarre record:

Besparje in penny op grutte folumes yn PostgreSQL
Minus 6% folume, Grut!

Mar alles is fansels net sa rooskleurich - ommers, yn yndeksen kinne wy ​​de folchoarder fan fjilden net feroarje, en dus "yn it algemien" (pg_total_relation_size) ...

Besparje in penny op grutte folumes yn PostgreSQL
... hjir ek noch bewarre 1.5%sûnder in inkele rigel fan koade te feroarjen. Ja, ja!

Besparje in penny op grutte folumes yn PostgreSQL

Ik merk op dat de boppesteande opsje foar it regeljen fan fjilden net it feit is dat it de meast optimale is. Om't jo guon blokken fjilden net wolle "skuorre" om estetyske redenen - bygelyks in pear (pack, recno), dat is de PK foar dizze tabel.

Yn 't algemien is it bepalen fan' e "minimum" regeling fan fjilden in frij ienfâldige taak "brute krêft". Dêrom kinne jo noch bettere resultaten krije fan jo gegevens dan ús - besykje it!

Boarne: www.habr.com

Add a comment