Fortsett med temaet for registrering av store datastreamer, tatt opp , her ser vi pÄ metodene for Ä redusere den «fysiske» stÞrrelsen pÄ lagret i PostgreSQL, og deres innvirkning pÄ serverytelsen.
Det vil bli snakk om TOAST-innstillinger og datalignering. «I gjennomsnitt» vil disse metodene ikke spare sĂ„ mye ressurser, men â helt uten modifikasjon av applikasjonskoden.

Likevel, vĂ„r erfaring har vist seg Ă„ vĂŠre ganske produktiv pĂ„ dette omrĂ„det, ettersom lagringslĂžsningen for nesten all overvĂ„king i sin natur er for det meste append-only nĂ„r det gjelder de innspilte dataene. Og hvis du er interessert i hvordan du kan lĂŠre databasen Ă„ skrive til disk i stedet for 200MB/s halvparten sĂ„ mye â vennligst les videre.
SmÄ hemmeligheter om store data
I profileringen av , fÄr den regelmessig tekstpakker fra loggene.
Og siden , hvis databaser vi overvÄker, er et flerfaglig produkt med komplekse datamodeller, er forespÞrslene for Ä oppnÄ maksimal ytelse helt klart . SÄ stÞrrelsen pÄ hver enkelt forespÞrsel eller den resulterende utfÞrelsesplanen i loggene som kommer til oss viser seg Ä vÊre «i gjennomsnitt» ganske stor.
La oss ta en titt pĂ„ strukturen til en av tabellene hvor vi skriver «rÄ» data â det vil si, den originale teksten fra loggoppfĂžringen:
CREATE TABLE rawdata_orig(
pack -- PK
uuid NOT NULL
, recno -- PK
smallint NOT NULL
, dt -- partisjonsnĂžkkel
date
, data -- det viktigste
text
, PRIMARY KEY(pack, recno)
);En typisk tabell (allerede partisjonert, selvfÞlgelig, sÄ dette er en mal for partisjonen), der det viktigste er teksten. Noen ganger ganske omfattende.
La oss huske at den «fysiske» stÞrrelsen pÄ en post i PG ikke kan ta mer enn én datakilde, men den «logiske» stÞrrelsen er helt forskjellig. For Ä skrive en stor verdi i et felt (varchar/text/bytea) brukes :
PostgreSQL bruker en fast side stÞrrelse (vanligvis 8 KB), og tillater ikke at tuppler tar opp flere sider. Dermed er det ikke mulig Ä lagre veldig store felter direkte. For Ä overvinne denne begrensningen, blir store feltverdier komprimert og/eller delt opp i flere fysiske rader. Dette skjer usynlig for brukeren og pÄvirker i liten grad stÞrstedelen av serverkoden. Denne metoden er kjent som TOAST ...
Faktisk, for hver tabell med "potensielt store" felt, opprettes automatisk av hver "stor" post i segmenter pÄ 2KB:
TOAST(
chunk_id
integer
, chunk_seq
integer
, chunk_data
bytea
, PRIMARY KEY(chunk_id, chunk_seq)
); Det vil si, hvis vi mÄ skrive en rad med et "stort" verdi data, vil den faktiske innspillingen skje ikke bare i hovedtabellen og dens PK, men ogsÄ i TOAST og dens PK.
Redusere TOAST-pÄvirkningen
Men de fleste postene vĂ„re er likevel ikke sĂ„ store, de burde passe inn i 8KB â hvordan kan vi spare pĂ„ dette?..
Her kommer attributtet til hjelp i tabellkolonnen:
- EXTENDED tillater bÄde komprimering og separat lagring. Dette er standardalternativet for de fleste datatyper som er kompatible med TOAST. FÞrst forsÞkes det Ä komprimere, deretter - Ä lagre utenfor tabellen, hvis raden fortsatt er for stor.
- MAIN tillater komprimering, men ikke separat lagring. (Faktisk vil separat lagring likevel bli utfÞrt for slike kolonner, men bare som en siste utvei, nÄr det ikke er andre mÄter Ä redusere raden sÄ den fÄr plass pÄ siden.)
Faktisk er dette akkurat det vi trenger for tekst â komprimere maksimalt, og hvis det absolutt ikke fĂ„r plass â flytte til TOAST. Dette kan gjĂžres direkte "on the fly", med Ă©n kommando:
ALTER TABLE rawdata_orig ALTER COLUMN data SET STORAGE MAIN;Hvordan vurdere effekten
Siden datatroppene endres hver dag, kan vi ikke sammenligne absolutte tall, men relativt jo mindre andel vi har skrevet til TOAST â jo bedre. Men det er en fare â jo stĂžrre "fysisk" volum av hver enkelt post, jo "bredere" blir indeksen, fordi man mĂ„ dekke flere datalagsider.
Seksjon fĂžr endringer:
heap = 37GB (39%)
TOAST = 54GB (57%)
PK = 4GB ( 4%)
Seksjon etter endringer:
heap = 37GB (67%)
TOAST = 16GB (29%)
PK = 2GB ( 4%)Faktisk har vi begynt Ä skrive til TOAST 2 ganger sjeldnere, som lettet ikke bare disken, men ogsÄ CPU-en:


Jeg bemerker at vi ogsĂ„ «leser» disken mindre, og ikke bare «skriver» â fordi nĂ„r vi setter inn en post i en tabell, mĂ„ vi «lese» ogsĂ„ en del av treet til hver av indeksene for Ă„ fastslĂ„ dens fremtidige posisjon i dem.
Hvem har det bra med PostgreSQL 11
Etter oppdateringen til PG11 bestemte vi oss for Ä fortsette med «tuning» av TOAST og la merke til at fra denne versjonen er parameteren blitt tilgjengelig for innstilling :
TOAST-behandlingskoden aktiveres bare nÄr verdien av raden som skal lagres i tabellen, er stÞrre enn TOAST_TUPLE_THRESHOLD byte (vanligvis 2 Kb). TOAST-koden vil komprimere og/eller flytte feltverdier utenfor tabellen inntil verdien av raden er mindre enn TOAST_TUPLE_TARGET byte (variabel stÞrrelse, ogsÄ vanligvis 2 Kb) eller reduksjon av volumet ikke lenger er mulig.
Vi konkluderte med at dataene vÄre vanligvis enten er «ganske korte» eller umiddelbart «veldig lange», sÄ vi bestemte oss for Ä begrense oss til den minimal-mulige verdien:
ALTER TABLE rawplan_orig SET (toast_tuple_target = 128);La oss se hvordan de nye innstillingene pÄvirket diskbelastningen etter omkonfigurering:

Ikke verst! Gjennomsnittlig diskkÞ redusert med omtrent 1,5 ganger, og «belegg» pÄ disken har gÄtt ned med 20%! Men kan det ha pÄvirket CPU-en pÄ noen mÄte?

I det minste har det definitivt ikke blitt verre. Selv om det er vanskelig Ă„ bedĂžmme, hvis selv slike volumer fortsatt ikke kan lĂžfte den gjennomsnittlige CPU-belastningen over 5%.
Fra bytte av plass pÄ leddene endres summen!
Som kjent sparer en kopeck en rubel, og med vĂ„re lagringsvolumer pĂ„ omtrent 10TB/mĂ„ned kan selv en liten optimalisering gi god profitt. Derfor la vi merke til den fysiske strukturen av vĂ„re data â hvordan spesifikt felt blir «pakket» inn i hver post i hver tabell.
Fordi pÄ grunn av dette direkte :
Mange arkitekturer forutsetter justering av data ved grensen av registerstÞrrelser. For eksempel, pÄ et 32-bits x86-system vil heltall (type integer, tar 4 byte) bli justert til en grense for 4-byte ord, sÄ vel som dobbelt presisjon flyttall (type double precision, 8 byte). Og pÄ et 64-bits system vil verdiene for double bli justert ved grensen for 8-byte ord. Dette er enda en grunn til inkompatibilitet.
PÄ grunn av justeringen av tabellraden avhenger stÞrrelsen av rekkefÞlgen pÄ feltene. Vanligvis er denne effekten ikke sÄ merkbar, men i noen tilfeller kan det fÞre til en betydelig Þkning i stÞrrelsen. For eksempel, hvis man plasserer feltene av typene char(1) og integer om hverandre, vil det vanligvis mangle 3 byte mellom dem.
La oss starte med syntetiske modeller:
SELECT pg_column_size(ROW(
'0000-0000-0000-0000-0000-0000-0000-0000'::uuid
, 0::smallint
, '2019-01-01'::date
));
-- 48 byte
SELECT pg_column_size(ROW(
'2019-01-01'::date
, '0000-0000-0000-0000-0000-0000-0000-0000'::uuid
, 0::smallint
));
-- 46 byteHvor kom de ekstra byte fra i det fĂžrste tilfellet? Det er enkelt â den 2-byte smallint justeres til en 4-byte grense fĂžr det neste feltet, og nĂ„r det kommer sist, er det ingenting Ă„ justere.
I teorien er alt bra og man kan flytte pÄ feltene som man vil. La oss teste med virkelige data med et eksempel fra en av tabellene, hvor den daglige seksjonen tar 10-15GB.
Den opprinnelige strukturen:
CREATE TABLE public.plan_20190220
(
-- Arvet fra tabell plan: pack uuid NOT NULL,
-- Arvet fra tabell plan: recno smallint NOT NULL,
-- Arvet fra tabell plan: host uuid,
-- Arvet fra tabell plan: ts timestamp with time zone,
-- Arvet fra tabell plan: exectime numeric(32,3),
-- Arvet fra tabell plan: duration numeric(32,3),
-- Arvet fra tabell plan: bufint bigint,
-- Arvet fra tabell plan: bufmem bigint,
-- Arvet fra tabell plan: bufdsk bigint,
-- Arvet fra tabell plan: apn uuid,
-- Arvet fra tabell plan: ptr uuid,
-- Arvet fra tabell 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)Seksjonen etter endring av rekkefĂžlgen pĂ„ kolonnene â er akkurat de samme feltene, bare i en annen rekkefĂžlge.:
CREATE TABLE public.plan_20190221
(
-- Arvet fra tabell plan: dt date NOT NULL,
-- Arvet fra tabell plan: ts timestamp with time zone,
-- Arvet fra tabell plan: pack uuid NOT NULL,
-- Arvet fra tabell plan: recno smallint NOT NULL,
-- Arvet fra tabell plan: host uuid,
-- Arvet fra tabell plan: apn uuid,
-- Arvet fra tabell plan: ptr uuid,
-- Arvet fra tabell plan: bufint bigint,
-- Arvet fra tabell plan: bufmem bigint,
-- Arvet fra tabell plan: bufdsk bigint,
-- Arvet fra tabell plan: exectime numeric(32,3),
-- Arvet fra tabell 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) Den totale stĂžrrelsen pĂ„ seksjonen bestemmes av antall «fakta» og avhenger kun av eksterne prosesser, sĂ„ vi deler stĂžrrelsen pĂ„ heap (pg_relation_size) for the number of entries in it â that is, we will get the average size of the actual stored record:

Minus 6% of the volume, excellent!
But not everything is so rosy â after all, we cannot change the order of fields in indexes, and therefore "in general" (pg_total_relation_size)âŠ

⊠yet here, too, we saved 1.5%, without changing a single line of code. Indeed!

I should note that the arrangement of fields given above is not necessarily the most optimal. Because some blocks of fields are not desirable to "tear apart" for aesthetic reasons â for example, a pair (pack, recno), which is the PK for this table.
In general, determining the "minimal" arrangement of fields is quite a simple "brute force" task. Therefore, you can achieve even better results on your data than we did â give it a try!
Kilde: habr.com
