JĂ€tkates teemat suurte andmevoogude salvestamisest, tĂ”statatud , 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.

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 , saab regulaarselt logidelt tekstipakette.
Ja kuna , kelle andmebaase me jÀlgime, on mitmekomponendiline toode keeruliste andmestruktuuridega, siis on ka pÀringud maksimaalse tulemuslikkuse saavutamiseks tÀiesti sellised . 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 :
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 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 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:


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

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?

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 mÔjub otseselt :
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 baitiKust 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:

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)âŠ

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

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
