Vi skriver i PostgreSQL på sublight: 1 vært, 1 dag, 1 TB

For nylig fortalte jeg dig hvordan, ved hjælp af standardopskrifter øge ydelsen af ​​SQL-læseforespørgsler fra PostgreSQL-databasen. I dag vil vi tale om hvordan optagelse kan gøres mere effektivt i databasen uden at bruge nogen "twists" i konfigurationen - blot ved at organisere datastrømmene korrekt.

Vi skriver i PostgreSQL på sublight: 1 vært, 1 dag, 1 TB

#1. Sektionering

En artikel om hvordan og hvorfor det er værd at organisere anvendt opdeling "i teorien" allerede har været, her vil vi tale om praksis med at anvende nogle tilgange inden for vores overvågningstjeneste for hundredvis af PostgreSQL-servere.

"Ting fra svundne dage..."

Som enhver MVP startede vores projekt oprindeligt under en forholdsvis let belastning - overvågning blev kun udført for et dusin af de mest kritiske servere, alle borde var relativt kompakte... Men som tiden gik, blev antallet af overvågede værter flere og mere, og endnu en gang prøvede vi at lave noget med en af borde 1.5TB i størrelse, indså vi, at selvom det var muligt at fortsætte med at leve sådan her, var det meget ubelejligt.

Tiderne var næsten som episke tider, forskellige versioner af PostgreSQL 9.x var relevante, så al partitionering skulle udføres "manuelt" - gennem tabel arv og triggere routing med dynamik EXECUTE.

Vi skriver i PostgreSQL på sublight: 1 vært, 1 dag, 1 TB
Den resulterende løsning viste sig at være universel nok til at den kunne oversættes til alle tabeller:

  • En tom "header" overordnet tabel blev erklæret, som beskrev alle nødvendige indekser og triggere.
  • Optegnelsen fra klientens synspunkt blev lavet i "root" tabellen, og internt vha routing trigger BEFORE INSERT posten blev "fysisk" indsat i den påkrævede sektion. Hvis der ikke var sådan noget endnu, fangede vi en undtagelse og...
  • … ved hjælp af CREATE TABLE ... (LIKE ... INCLUDING ...) blev oprettet baseret på skabelonen for den overordnede tabel afsnit med en begrænsning på den ønskede datosåledes at når data hentes, udføres læsning kun i den.

PG10: første forsøg

Men partitionering gennem arv har historisk set ikke været velegnet til at håndtere en aktiv skrivestrøm eller et stort antal underordnede partitioner. For eksempel kan du huske, at algoritmen til at vælge den nødvendige sektion havde kvadratisk kompleksitet, at det fungerer med 100+ sektioner, forstår du selv hvordan...

I PG10 blev denne situation i høj grad optimeret ved at implementere support native partitionering. Derfor forsøgte vi straks at anvende det umiddelbart efter migrering af lageret, men...

Som det viste sig efter at have gravet igennem manualen, er den oprindeligt opdelte tabel i denne version:

  • understøtter ikke indeksbeskrivelser
  • understøtter ikke triggere på den
  • kan ikke være nogens "efterkommer"
  • ikke understøtter INSERT ... ON CONFLICT
  • kan ikke generere et afsnit automatisk

Efter at have modtaget et smertefuldt slag i panden med en rive, indså vi, at det ville være umuligt at gøre uden at ændre ansøgningen, og udsatte yderligere forskning i seks måneder.

PG10: anden chance

Så vi begyndte at løse de problemer, der opstod én efter én:

  1. Fordi udløser og ON CONFLICT Vi fandt ud af, at vi stadig havde brug for dem hist og her, så vi lavede en mellemstadie for at finde ud af dem proxy tabel.
  2. Slip af med "routing" i triggere - altså fra EXECUTE.
  3. De tog den ud separat skabelontabel med alle indekserså de ikke engang er til stede i proxy-tabellen.

Vi skriver i PostgreSQL på sublight: 1 vært, 1 dag, 1 TB
Til sidst, efter alt dette, partitionerede vi hovedtabellen indbygget. Oprettelsen af ​​et nyt afsnit er stadig overladt til ansøgningens samvittighed.

"Save" ordbøger

Som i ethvert analytisk system havde vi også "fakta" og "nedskæringer" (ordbøger). I vores tilfælde handlede de i denne egenskab f.eks. skabelonens krop lignende langsomme forespørgsler eller selve forespørgslens tekst.

“Fakta” ​​var allerede i lang tid opdelt efter dag, så vi slettede roligt forældede afsnit, og de generede os ikke (logfiler!). Men der var et problem med ordbøger...

Ikke for at sige, at der var mange af dem, men ca 100 TB "fakta" resulterede i en 2.5 TB ordbog. Du kan ikke bekvemt slette noget fra sådan en tabel, du kan ikke komprimere den i tilstrækkelig tid, og skrivning til den blev gradvist langsommere.

Som en ordbog... i den skal hver post præsenteres nøjagtigt én gang... og det er korrekt, men!.. Ingen forhindrer os i at have en separat ordbog for hver dag! Ja, dette medfører en vis redundans, men det tillader:

  • skrive/læse hurtigere på grund af mindre sektionsstørrelse
  • bruger mindre hukommelse ved at arbejde med mere kompakte indekser
  • gemme mindre data på grund af muligheden for hurtigt at fjerne forældede

Som et resultat af hele komplekset af foranstaltninger CPU-belastning faldt med ~30%, diskbelastning med ~50%:

Vi skriver i PostgreSQL på sublight: 1 vært, 1 dag, 1 TB
Samtidig fortsatte vi med at skrive præcis det samme ind i databasen, bare med mindre belastning.

#2. Databaseudvikling og refactoring

Så vi blev enige om, hvad vi har hver dag har sit eget afsnit med data. Rent faktisk, CHECK (dt = '2018-10-12'::date) — og der er en partitioneringsnøgle og betingelsen for, at en post falder ind i en bestemt sektion.

Da alle rapporter i vores tjeneste er bygget i sammenhæng med en bestemt dato, har indekserne for dem siden "ikke-opdelte tider" været alle typer (Server, dato, planskabelon), (Server, dato, Plan node), (dato, Fejlklasse, Server), ...

Men nu bor de på hver sektion dine kopier hvert sådant indeks... Og inden for hver sektion dato er en konstant... Det viser sig, at nu er vi i hvert sådant indeks Indtast blot en konstant som et af felterne, hvilket øger både dets volumen og søgetiden for det, men ikke giver noget resultat. De overlod riven til sig selv, ups...

Vi skriver i PostgreSQL på sublight: 1 vært, 1 dag, 1 TB
Optimeringsretningen er indlysende – enkel fjern datofeltet fra alle indekser på opdelte borde. I betragtning af vores mængder er gevinsten ca 1 TB/uge!

Lad os nu bemærke, at denne terabyte stadig skulle optages på en eller anden måde. Det vil sige, vi også disken skulle nu indlæse mindre! Dette billede viser tydeligt effekten opnået fra rengøringen, som vi brugte en uge til:

Vi skriver i PostgreSQL på sublight: 1 vært, 1 dag, 1 TB

#3. "Spredning" af spidsbelastningen

En af de store problemer ved indlæste systemer er redundant synkronisering nogle operationer, der ikke kræver det. Nogle gange "fordi de ikke lagde mærke til det", nogle gange "det var nemmere på den måde", men før eller siden skal du af med det.

Lad os zoome ind på det forrige billede og se, at vi har en disk "pumper" under belastningen med dobbelt amplitude mellem tilstødende prøver, hvilket klart "statistisk" ikke burde ske med et sådant antal operationer:

Vi skriver i PostgreSQL på sublight: 1 vært, 1 dag, 1 TB

Dette er ret nemt at opnå. Vi er allerede begyndt at overvåge næsten 1000 servere, hver behandles af en separat logisk tråd, og hver tråd nulstiller den akkumulerede information, der skal sendes til databasen med en bestemt frekvens, noget som dette:

setInterval(sendToDB, interval)

Problemet her ligger netop i, at alle tråde starter på nogenlunde samme tid, så deres afsendelsestidspunkter falder næsten altid sammen "to the point". Ups #2...

Heldigvis er dette ret nemt at rette, tilføjelse af et "tilfældigt" opløb Med tiden:

setInterval(sendToDB, interval * (1 + 0.1 * (Math.random() - 0.5)))

#4. Vi cacher, hvad vi har brug for

Det tredje traditionelle højbelastningsproblem er ingen cache hvor han er kunne at være.

For eksempel gjorde vi det muligt at analysere i form af planknudepunkter (alle disse Seq Scan on users), men tror straks, at de for det meste er de samme - de har glemt.

Nej, selvfølgelig bliver der ikke skrevet noget til databasen igen, dette afbryder triggeren med INSERT ... ON CONFLICT DO NOTHING. Men disse data når stadig databasen, og det er unødvendigt læsning for at tjekke for konflikt må gøre. Ups #3...

Forskellen i antallet af poster sendt til databasen før/efter caching er aktiveret er indlysende:

Vi skriver i PostgreSQL på sublight: 1 vært, 1 dag, 1 TB

Og dette er det ledsagende fald i lagerbelastning:

Vi skriver i PostgreSQL på sublight: 1 vært, 1 dag, 1 TB

I alt

"Terabyte-per-dag" lyder bare skræmmende. Hvis du gør alt rigtigt, så er det bare 2^40 bytes / 86400 sekunder = ~12.5 MB/sat selv desktop IDE skruer holdt. 🙂

Men seriøst, selv med en tidoblet "skævhed" af belastningen i løbet af dagen, kan du nemt møde mulighederne i moderne SSD'er.

Vi skriver i PostgreSQL på sublight: 1 vært, 1 dag, 1 TB

Kilde: www.habr.com

Tilføj en kommentar