Kogume kokku vÀikese raha suuremate mahtude puhul PostgreSQL-is

JĂ€tkates teemat suurte andmevoogude salvestamisest, tĂ”statatud eelnevas artiklis sektsioneerimise kohta, vaatleme selles, kuidas vĂ€heneda „fĂŒĂŒsilist“ suurust salvestatud PostgreSQL-is ja nende mĂ”ju serveri tulemuslikkusele.

Jutt lĂ€heb TOAST seadistustest ja andmete joondamisest. „Keskmiselt” vĂ”imaldavad need meetodid sÀÀsta mitte liiga palju ressursse, kuid — tĂ€iesti ilma rakenduskoodi modifitseerimiseta.

Kogume kokku vÀikese raha suuremate mahtude puhul PostgreSQL-is
Kuid meie kogemus osutus selles osas ĂŒsna produktiivseks, kuna praktiliselt iga jĂ€lgimise salvestus on oma olemuselt suurel mÀÀral ainult append-only salvestatavate andmete osas. Ja kui teid huvitab, kuidas Ă”petada andmebaasi kirjutama kettale asemel 200 MB/s kaks korda vĂ€hem — tulge edasi.

Suurte andmete vÀikesed saladused

Meie teenuse profilatsiooni jÀrgi, saab regulaarselt logidelt tekstipakette.

Ja kuna SBIS kompleks, kelle andmebaase me jĂ€lgime, on mitmekomponendiline toode keeruliste andmestruktuuridega, siis on ka pĂ€ringud maksimaalse tulemuslikkuse saavutamiseks tĂ€iesti sellised „mitme köitega“ keerulise algoritmilise loogikaga. Seega on iga individuaalse pĂ€ringu vĂ”i saadud tĂ€itmisplaani maht meie logis "keskmiselt" piisavalt suur.

Vaatame ĂŒhte tabeli struktuuri, kuhu kirjutame "tooreid" andmeid — st originaalteksti logikirjest:

CREATE TABLE rawdata_orig(
  pack -- PK
    uuid NOT NULL
, recno -- PK
    smallint NOT NULL
, dt -- sektsiooni vÔti
    date
, data -- kÔige olulisem
    text
, PRIMARY KEY(pack, recno)
);

TĂŒĂŒpiline selline tabel (juba seotud, loomulikult, seega on see — sektsiooni mall), kus kĂ”ige olulisem on tekst. Kohati piisavalt mahukas.

Kasutame meeles, et PG-s ĂŒhe kirje "fĂŒĂŒsiline" suurus ei saa ĂŒletada ĂŒhte andmelehekĂŒlge, kuid "loogiline" suurus on sootuks teine asi. Mahuliste vÀÀrtuste (varchar/text/bytea) salvestamiseks vĂ€ljal kasutatakse tehnoloogiat TOAST:

PostgreSQL kasutab fikseeritud lehe suurust (tavaliselt 8 KB) ja ei luba tuple'idel vĂ”tta mitu lehte. SeetĂ”ttu ei saa vĂ€ga suuri vĂ€ljade vÀÀrtusi otse salvestada. Selle piirangu ĂŒletamiseks surutakse suured vĂ€ljade vÀÀrtused ja/vĂ”i jagatakse need mitmeks fĂŒĂŒsiliseks reaks. See toimub kasutajale mĂ€rkamatult ning mĂ”jutab suure osa serveri koodist vĂ€he. Seda meetodit tuntakse kui TOAST 


Tegelikult luuakse iga 'potentsiaalselt suure' vĂ€lja tabeli jaoks automaatselt paarne tabel 'tĂŒkeldamisega' iga 'suure' kirje kohta segmentide kaupa 2KB:

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

See tÀhendab, et kui peame kirjutama rea, millel on 'suur' vÀÀrtus data, siis tÔeline salvestamine toimub mitte ainult pÔhitaotlemises ja selle PK-s, vaid ka TOAST-is ja selle PK-s.

VÀhendame TOAST-i mÔju

Kuid enamik kirjeid ei ole siiski nii suured, 8KB peaks olema piisav — kuidas selle pealt kokku hoida?..

Siin tuleb meile appi atribuut STORAGE tabeli veerus:

  • EXTENDED lubab nii pakkimist kui ka eraldi salvestamist. See on standardne valik enamikule TOAST-iga ĂŒhilduvatest andmetĂŒĂŒpidest. Esiteks proovitakse teha tihendamist, seejĂ€rel - salvestamist tabeli vĂ€listesse osadesse, kui rida on endiselt liiga suur.
  • PEA lubab tihendamist, kuid mitte eraldi salvestamist. (Tegelikult tehakse eraldi salvestamine selliste vĂ€ljade puhul siiski, kuid ainult ÀÀrmuslikul juhul, kui pole muid vĂ”imalusi, et rida vĂ€hendada nii, et see mahtuks lehekĂŒljele.)

Tegelikult on see tĂ€pselt see, mida me tekstiga vajame - maksimaalselt tihendada, ja kui ei mahu, siis viia TOAST-i. Seda saab teha otse "lennul", ĂŒhe kĂ€suga:

ALTER TABLE rawdata_orig ALTER COLUMN data SET STORAGE MAIN;

Kuidas hinnata mÔju

Kuna andmevoog muutub iga pĂ€ev, ei saa me vĂ”rrelda absoluutseid numbreid, vaid suhtelisi. mida vĂ€iksem osa me TOAST-i salvestasime - seda parem. Kuid siin on oht - mida suurem on meie iga eraldi kirje "fĂŒĂŒsiline" maht, seda "laiem" muutub indeks, kuna peame katma rohkem andmelehti.

Sektsioon enne muudatusi:

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

Sektsioon pÀrast muudatusi:

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

Tegelikult, me oleme hakanud TOAST-i kirjutama kaks korda harvemini, mis leevendas mitte ainult ketast, vaid ka CPU-d:

Kogume kokku vÀikese raha suuremate mahtude puhul PostgreSQL-is
Kogume kokku vÀikese raha suuremate mahtude puhul PostgreSQL-is
Tahan mĂ€rkida, et me oleme hakanud vĂ€hem ka ketast «lugema», mitte ainult «kirjutama» — kuna mingi tabelisse kirje sisestamisel tuleb lugeda ka osa iga indeksi puust, et mÀÀrata selle tulevane positsioon nendes.

Kellele elada PostgreSQL 11 peal hÀsti

PĂ€rast PG11-le ĂŒleminekut otsustasime TOAST-i «tuningut» edasi viia ja panime tĂ€hele, et alates sellest versioonist on seadistatav parameter toast_tuple_target:

TOAST-i töötlemise kood aktiveerub ainult siis, kui tabelisse salvestatav ridade vÀÀrtus ĂŒletab TOAST_TUPLE_THRESHOLD baitides (tavaliselt 2 KB). TOAST-i kood hakkab tihendama vĂ”i viima vĂ€lja vÀÀrtusi tabelist, kuni ridade vÀÀrtus jÀÀb alla TOAST_TUPLE_TARGET baitide (muutuva suurusega, samuti tavaliselt 2 KB) vĂ”i kuni edasine vĂ€hendamine pole vĂ”imalik.

Otsustasime, et meie andmed on kas «tĂ€iesti lĂŒhikesed» vĂ”i «vĂ€ga pikad», seega otsustasime piirduda minimaalse vĂ”imaliku vÀÀrtusega:

ALTER TABLE rawplan_orig SET (toast_tuple_target = 128);

Vaadake, kuidas uued seaded on mÔjutanud ketta laadimist pÀrast seadistuste muutmist:

Kogume kokku vÀikese raha suuremate mahtude puhul PostgreSQL-is
Pole paha! Keskmine ketta jĂ€rjekord lĂŒhenes umbes 1.5 korda ja ketta „kasutatus“ vĂ€henes 20% vĂ”rra! Aga vĂ”ib-olla mĂ”jutas see kuidagi CPU-d?

Kogume kokku vÀikese raha suuremate mahtude puhul PostgreSQL-is
Igal juhul halvemaks ei lÀinud. Kuigi on raske öelda, kui isegi sellised mahud ei suuda keskmise CPU koormust tÔsta 5%.

Kohavahetusega muutub ka summa!

Nagu teada, hoiab rubla rubla ja meie salvestusmahu korral 10TB/kuus vĂ”ib isegi vĂ€ike optimeerimine anda head kasu. SeetĂ”ttu pöörasime tĂ€helepanu oma andmete fĂŒĂŒsilisele struktuurile — kuidas tĂ€pselt vĂ€ljad on „paigutatud“ iga tabeli sissekande sees.

Sest andmete joondamine mÔjub otseselt tulemuslikule mahule:

Paljud arhitektuurid nĂ”uavad andmete joondamist masinasĂ”nade piiride jĂ€rgi. NĂ€iteks 32-bitises x86 sĂŒsteemis joondatakse tĂ€isarvud (tĂŒĂŒp integer, 4 baiti) 4-baidiste sĂ”nade piiride jĂ€rgi, nagu ka topelt tĂ€psuse ujukomaarvud (tĂŒĂŒp double precision, 8 baiti). 64-bitises sĂŒsteemis joondatakse double vÀÀrtused 8-baidiste sĂ”nade piiride jĂ€rgi. See on veel ĂŒks pĂ”hjus, miks tekib ĂŒhilduvusprobleeme.

Joondamise tĂ”ttu sĂ”ltub tabeli rea suurus vĂ€ljade paigutuse jĂ€rjekorrast. TĂŒĂŒpiliselt pole see efekt vĂ€ga mĂ€rgatav, kuid mĂ”nel juhul vĂ”ib see pĂ”hjustada mĂ€rkimisvÀÀrse suuruse suurenemise. NĂ€iteks, kui segada char(1) ja integer tĂŒĂŒpi vĂ€lju, kaob nende vahele tavaliselt 3 baiti.

Alustame sĂŒnteetiliste mudelitega:

SELECT pg_column_size(ROW(
  '0000-0000-0000-0000-0000-0000-0000-0000'::uuid
, 0::smallint
, '2019-01-01'::date
));
-- 48 baiti

SELECT pg_column_size(ROW(
  '2019-01-01'::date
, '0000-0000-0000-0000-0000-0000-0000-0000'::uuid
, 0::smallint
));
-- 46 baiti

Kust tulid kaks ĂŒleliigset baiti esimeses juhul? See on lihtne — 2-baidine smallint joondatakse 4-baidise piiri jĂ€rgi enne jĂ€rgmist vĂ€lja, kuid kui see on viimasena, ei ole joondamiseks mitte midagi ja pole vajadust.

Teoorias on kĂ”ik hĂ€sti ja saame vĂ€ljad paigutada kuidas tahes. Kontrollime reaalsed andmed ĂŒhe tabeli nĂ€itel, mille pĂ€evane sektsioon hĂ”ivab 10-15 GB.

Algne struktuur:

CREATE TABLE public.plan_20190220
(
-- PĂ€randatud tabelist plan:  pack uuid NOT NULL,
-- PĂ€randatud tabelist plan:  recno smallint NOT NULL,
-- PĂ€randatud tabelist plan:  host uuid,
-- PĂ€randatud tabelist plan:  ts timestamp with time zone,
-- PĂ€randatud tabelist plan:  exectime numeric(32,3),
-- PĂ€randatud tabelist plan:  duration numeric(32,3),
-- PĂ€randatud tabelist plan:  bufint bigint,
-- PĂ€randatud tabelist plan:  bufmem bigint,
-- PĂ€randatud tabelist plan:  bufdsk bigint,
-- PĂ€randatud tabelist plan:  apn uuid,
-- PĂ€randatud tabelist plan:  ptr uuid,
-- PĂ€randatud tabelist 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)

Sektsioon pĂ€rast veergude jĂ€rjekorra muutmist — tĂ€pselt samad vĂ€ljad, ainult jĂ€rjekord on teine:

LOO KAVA public.plan_20190221
(
-- PĂ€randatud tabelist plan: dt date NOT NULL,
-- PĂ€randatud tabelist plan: ts timestamp with time zone,
-- PĂ€randatud tabelist plan: pack uuid NOT NULL,
-- PĂ€randatud tabelist plan: recno smallint NOT NULL,
-- PĂ€randatud tabelist plan: host uuid,
-- PĂ€randatud tabelist plan: apn uuid,
-- PĂ€randatud tabelist plan: ptr uuid,
-- PĂ€randatud tabelist plan: bufint bigint,
-- PĂ€randatud tabelist plan: bufmem bigint,
-- PĂ€randatud tabelist plan: bufdsk bigint,
-- PĂ€randatud tabelist plan: exectime numeric(32,3),
-- PĂ€randatud tabelist 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)
)
PÄRANDAB (public.plan)

Jaotise kogumaht mÀÀratakse "faktide" arvu alusel ja sĂ”ltub ainult vĂ€listest protsessidest, seega jagame heap'i suuruse (pg_relation_size) selle salvestuste arvuga — seega saame reaalse salvestuse keskmine suurus:

Kogume kokku vÀikese raha suuremate mahtude puhul PostgreSQL-is
Miinus 6% mahust, suurepÀrane!

Aga kĂ”ik ei ole muidugi nii roosiline — sest indeksite puhul ei saa me vĂ€ljade jĂ€rjekorda muuta, seega "ĂŒldiselt" (pg_total_relation_size)


Kogume kokku vÀikese raha suuremate mahtude puhul PostgreSQL-is

 siiski ka siin sÀÀstsime 1,5%, muutes koodis ei tilkagi. TĂ€pselt nii!

Kogume kokku vÀikese raha suuremate mahtude puhul PostgreSQL-is

Tahan mĂ€rkida, et eespool toodud vĂ€ljade paigutus ei pruugi olla kĂ”ige optimaalsem. Sest mĂ”ningaid vĂ€ljade bloke ei taha juba esteetilistel pĂ”hjustel "katkestada" — nĂ€iteks paar (pack, recno), mis on selle tabeli PK.

KokkuvĂ”ttes on "minimaalsete" vĂ€ljade paigutuse mÀÀramine piisavalt lihtne "ĂŒletamisĂŒlesanne". Seega vĂ”ite oma andmete abil saavutada isegi paremaid tulemusi kui meie — proovige!

Allikas: habr.com

Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster