SÀÀstame sentti suurte mahtude puhul PostgreSQL-is

JĂ€tkates teemat suurte andmevoogude salvestamisest, mis tĂ”statati eelnevas artiklis jaotamisest, vaatame selles, kuidas vĂ€hendada PostgreSQL-is salvestatava "fĂŒĂŒsilise" suuruse ja nende mĂ”ju serveri jĂ”udlusele.

RÀÀgime TOAST seadistustest ja andmete joondamisest.. "Keskelt" need meetodid vÔimaldavad sÀÀsta mitte liiga palju ressursse, kuid tÀiesti ilma rakenduskoodi modifitseerimata.

SÀÀstame sentti suurte mahtude puhul PostgreSQL-is
Kuid meie kogemus osutus selles osas ĂŒsna produktiivseks, kuna peaaegu igasugune jĂ€lgimine on olemuselt enamasti append-only salvestatavate andmete osas. Ja kui teid huvitab, kuidas Ă”petada andmebaasi kirjutama kettale 200MB/s pool vĂ€hem - palun lugege edasi.

VĂ€ikesed saladused suurtest andmetest

Meie teenuse profiil, sellele saadetakse regulaarselt logidest teksti paketid..

Ja kuna SBIS-i kompleks, mille andmebaase me jĂ€lgime, on mitmekomponentne toode keeruliste andmestruktuuridega, siis tekivad ka pĂ€ringud maksimaalse jĂ”udluse saavutamiseks ĂŒpris selliseid "multivihikutes" keeruka algoritmilise loogikaga.Nii et iga eraldi pĂ€ringu vĂ”i tulemuse tĂ€itmise plaani maht, mis meile logisse jĂ”uab, osutub "keskmiselt" piisavalt suureks.

Vaadakem ĂŒhte tabeli struktuuri, kuhu kirjutame "toored" andmed - see tĂ€hendab algset tekstivĂ€li logikirjest:

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

TĂŒĂŒpiline selline tabel (juba jaotatud, loomulikult, seetĂ”ttu on see - sektsiooni mall), kus kĂ”ige olulisem on tekst. Aeg-ajalt piisavalt mahukas.

KĂŒll aga meenutame, et PG-s ĂŒhe salvestuse "fĂŒĂŒsiline" suurus ei saa olla suurem kui ĂŒks andmeleht, kuid "loogiline" suurus - see on hoopis teine asi. Mahuka vÀÀrtuse (varchar/text/bytea) salvestamiseks kasutatakse TOAST tehnoloogiat.:

PostgreSQL kasutab fikseeritud lehekĂŒlje suurust (tavaliselt 8 KB) ja ei luba ridadel hĂ”ivata mitut lehte. SeetĂ”ttu ei ole vĂ€ga suurte vĂ€ljade vÀÀrtuste otsene salvestamine vĂ”imalik. Selle piirangu ĂŒletamiseks tihendatakse ja/vĂ”i jagatakse suured vĂ€ljade vÀÀrtused mitmeks fĂŒĂŒsiliseks reale. See toimub kasutaja jaoks mĂ€rkamatult ning enamikule serveri koodist on mĂ”ju minimaalne. Seda meetodit tuntakse kui TOAST...

Tegelikult luuakse iga tabeli jaoks, millel on "potentsiaalselt suured" vĂ€ljad, automaatselt paaristabel "tĂŒkkide" jaoks iga "suur" kirje segmentideks 2KB:

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

Seega, kui peame salvestama rea, millel on "suur" vÀÀrtus data, siis toimub tegelik salvestamine mitte ainult pÔhitaotlusse ja selle PK-sse, vaid ka TOAST-i ja selle PK-sse.

VÀhendame TOAST-i mÔju

Kuid enamik meie kirjetest ei ole siiski nii suured, peaksid 8KB mahtuma — kuidas selle pealt sÀÀsta?..

Siin tuleb meile appi atribuuti STORAGE tabeli veerus:

  • EXTENDED lubab nii tihendamist kui ka eraldi salvestamist. See on standardne variant enamikule TOAST-iga ĂŒhilduvatele andmetĂŒĂŒpidele. Esiteks proovitakse teha tihendamine, seejĂ€rel salvestatakse vĂ€lja tabelist, kui rida on endiselt liiga suur.
  • MAIN lubab tihendamist, kuid mitte eraldi salvestamist. (Tegelikult toimub eraldi salvestamine, kuid ainult viimase abinĂ”una, kui muud vĂ”imalust ei ole, et rida piisavalt vĂ€ikeseks teha, et see lehte mahtuks.)

Tegelikult on see tĂ€pselt see, mida me vajame tekstile — maksimaalselt tihendada ja kui miski ei mahu — viia TOAST-i. Seda on vĂ”imalik teha otse "lendu", ĂŒhe kĂ€suga:

ALTER TABLE rawdata_orig ALTER COLUMN data SET STORAGE MAIN;

Kuidas hinnata mÔju

Kuna iga pĂ€ev andmevoog muutub, ei saa me vĂ”rrelda absoluutarve, kuid suhtelist: mida vĂ€iksem osa oleme TOAST-i salvestanud — seda parem. Kuid siin on oht — mida suurem on meie "fĂŒĂŒsiline" maht iga ĂŒksiku kirje puhul, seda "laiemaks" muutub indeks, kuna tuleb katta rohkem andmelehti.

Jaotis enne muudatusi:

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

Jaotis pÀrast muudatusi:

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

Tegelikult oleme hakkanud TOAST-i kirjutama 2 korda harvem, mis vÀhendas mitte ainult kettaruumihaldust, vaid ka CPU-koormust:

SÀÀstame sentti suurte mahtude puhul PostgreSQL-is
SÀÀstame sentti suurte mahtude puhul PostgreSQL-is
Tahan mĂ€rkida, et me hakkasime ketast vĂ€hem "lugema", mitte ainult "kirjutama" — kuna kirje lisamisel mĂ”nesse tabelisse tuleb "lugeda" ka osa iga indeksi puust, et mÀÀrata selle tulevane positsioon indeksites.

Kellele meeldib PostgreSQL 11-s elada

PĂ€rast PG11-le ĂŒleminekut otsustasime jĂ€tkata TOAST-i "tuningut" ja tĂ€helepanu juhtida sellele, et alates sellest versioonist on saanud seadistada parameetrit toast_tuple_target:

TOAST-i töötlemise kood kĂ€ivitub ainult siis, kui tabelis salvestatav stringi vÀÀrtus ĂŒletab TOAST_TUPLE_THRESHOLD baitides (tavaliselt 2 kB). TOAST-i kood hakkab tihendama ja / vĂ”i viima vĂ€lja andmevĂ€lja tabelist, kuni stringi vÀÀrtus on vĂ€iksem kui TOAST_TUPLE_TARGET baitides (muutuv vÀÀrtus, samuti tavaliselt 2 kB) vĂ”i kuni vĂ€iksemaks muutmine ei ole enam vĂ”imalik.

Otsustasime, et meie andmed on tavaliselt kas "ĂŒlimalt lĂŒhikesed" vĂ”i "vĂ€ga pikad", seega otsustasime jÀÀda minimaalselt vĂ”imalikule vÀÀrtusele:

ALTER TABLE rawplan_orig SET (toast_tuple_target = 128);

Vaatame, kuidas uued seaded on mĂ”jutanud kettaruumi pĂ€rast ĂŒmberseadmisi:

SÀÀstame sentti suurte mahtude puhul PostgreSQL-is
Pole paha! Keskmine kettajuhtimise ooteaeg vĂ€henes umbes 1,5 korda ja ketta "kasutatus" — 20% vĂ”rra! Kuid vĂ”ib-olla see kuidagi mĂ”jutas CPU-d?

SÀÀstame sentti suurte mahtude puhul PostgreSQL-is
Igal juhul ei saanud hullemaks. Kuigi, keeruline on hinnata, kui isegi sellised mahud ei suuda keskmist CPU-koormust tĂ”sta ĂŒle 5%.

Kohtade vahetamisega muutub summa
!

Nagu teada, kaitseb ĂŒhe kopika abiga rubla, ja meie salvestusmahud, mis on umbes 10TB/kuus suudavad isegi vĂ€ike optimeerimine pakkuda head kasu. SeetĂ”ttu pöörasime tĂ€helepanu oma andmete fĂŒĂŒsilisele struktuurile — kuidas konkreetselt "paigutatud" vĂ€ljad igas tabeli kirjes. Kuna andmete "joondamine"

mĂ”jutab otseselt saavutatud mahtu. Paljud arhitektuurid eeldavad andmete joondamist masinale vastavatesse piirteesse. NĂ€iteks 32-bitises x86 sĂŒsteemis on tĂ€isarvud (tĂŒĂŒp integer, mille suurus on 4 baiti) joondatud 4-baitsete sĂ”nade piiridesse, nagu ka topelt tĂ€psusega ujukomaarvud (tĂŒĂŒp double precision, 8 baiti). Ja 64-bitises sĂŒsteemis on topeltvÀÀrtused joondatud 8-baitsete sĂ”nade piiridesse. See on veel ĂŒks pĂ”hjus ĂŒhilduvuse probleemidele. mĂ”jub lĂ”ppmahule:

Paljud arhitektuurid nĂ€evad ette andmete joondamist masinasĂ”nade piiride jĂ€rgi. NĂ€iteks 32-bitises x86 sĂŒsteemis joondatakse tĂ€isarvud (tĂŒĂŒp integer, 4 bait) 4-baitiste sĂ”nade piirile, samuti ka kahekordse tĂ€psusega ujuvpunktarvud (tĂŒĂŒp double precision, 8 bait). 64-bitises sĂŒsteemis joondatakse double vÀÀrtused 8-baitiste sĂ”nade piirile. See on veel ĂŒks mittesobivuse pĂ”hjus.

Ridade tĂ”ttu sĂ”ltub tabeli rida tulpa jĂ€rjekorrast. Tavaliselt ei ole see efekt liiga silmatorkav, kuid mĂ”nel juhul vĂ”ib see pĂ”hjustada olulist suuruse suurenemist. NĂ€iteks, kui segada char(1) ja integer tĂŒĂŒpe, siis nende vahel kaob 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 ilmus esimeses nĂ€ites paar liigset baiti? KĂ”ik on lihtne — 2-baidine smallint joondatakse 4-baidise piiri jĂ€rgi enne jĂ€rgmist vĂ€lja, kuid kui see on viimane, siis ei ole joondamiseks midagi.

Teoorias on kĂ”ik hĂ€sti ja vĂ€lju saab paigutada meelevaldselt. Kontrollime reaalseid andmeid ĂŒhe tabeli nĂ€itel, mille pĂ€evase sektsiooni maht on 10–15GB.

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 erinev:

CREATE TABLE 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)
)
INHERITS (public.plan)

Sektsiooni kogumaht mÀÀratakse «faktide» arvu jĂ€rgi ja sĂ”ltub ainult vĂ€listest protsessidest, seega jagame heap'i suuruse (pg_relation_sizekandmisel on arvestatud kirjeid — seega saame keskmise suurusega salvestatud kirje:

SÀÀstame sentti suurte mahtude puhul PostgreSQL-is
Miinus 6% mahust, suurepÀrane!

Kuid kĂ”ik pole siiski nii roosiline — sest indeksites ei saa me vĂ€ljade jĂ€rjekorda muuta, seega "ĂŒldiselt" (pg_total_relation_size)


SÀÀstame sentti suurte mahtude puhul PostgreSQL-is
... siiski ka siin sÀÀstsime 1,5%, muutes koodis ainsatki rida. Nii see on!

SÀÀstame sentti suurte mahtude puhul PostgreSQL-is

Tahan mĂ€rkida, et eespool toodud vĂ€ljade paigutus ei ole kindlasti kĂ”ige optimaalsem. Kuna mĂ”ned vĂ€ljade plokid ei taha „katkestada“ juba esteetilistel pĂ”hjustel — nĂ€iteks paar (pack, recno), mis on selle tabeli PK.

Üldiselt on „minimaalse“ vĂ€ljade paigutuse mÀÀramine piisavalt lihtne „katsetamise“ ĂŒlesanne. Seega saate oma andmetega saavutada isegi paremaid tulemusi kui meil — proovige!

Allikas: habr.com

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster