Vi skriver i PostgreSQL på sublight: 1 vert, 1 dag, 1TB

Nylig fortalte jeg deg hvordan, ved å bruke standardoppskrifter øke ytelsen til SQL-lesespørringer fra PostgreSQL-databasen. I dag skal vi snakke om hvordan opptak kan gjøres mer effektivt i databasen uten å bruke noen "twists" i konfigurasjonen - ganske enkelt ved å organisere datastrømmene riktig.

Vi skriver i PostgreSQL på sublight: 1 vert, 1 dag, 1TB

#1. Seksjonering

En artikkel om hvordan og hvorfor det er verdt å organisere anvendt partisjonering "i teorien" har allerede vært, her vil vi snakke om praksisen med å anvende noen tilnærminger innenfor vår overvåkingstjeneste for hundrevis av PostgreSQL-servere.

"Ting fra gamle dager..."

Til å begynne med, som enhver MVP, startet prosjektet vårt under en ganske lett belastning - overvåking ble utført kun for de ti mest kritiske serverne, alle tabeller var relativt kompakte... Men etter hvert som tiden gikk, ble antallet overvåkede verter mer og mer , og nok en gang prøvde vi å gjøre noe med en av bord 1.5TB i størrelse, innså vi at selv om det var mulig å fortsette å leve slik, var det veldig upraktisk.

Tidene var nesten som episke tider, forskjellige versjoner av PostgreSQL 9.x var relevante, så all partisjonering måtte gjøres "manuelt" - gjennom tabell arv og triggere ruting med dynamikk EXECUTE.

Vi skriver i PostgreSQL på sublight: 1 vert, 1 dag, 1TB
Den resulterende løsningen viste seg å være universell nok til at den kunne oversettes til alle tabeller:

  • En tom "header" overordnet tabell ble erklært, som beskrev alle nødvendige indekser og triggere.
  • Oppføringen fra klientens synspunkt ble laget i "root"-tabellen, og internt vha rutingsutløser BEFORE INSERT posten ble "fysisk" satt inn i den nødvendige delen. Hvis det ikke var noe slikt ennå, fanget vi et unntak og...
  • … ved bruk av CREATE TABLE ... (LIKE ... INCLUDING ...) ble opprettet basert på malen til den overordnede tabellen seksjon med begrensning på ønsket datoslik at når data hentes, utføres lesing kun i den.

PG10: første forsøk

Men partisjonering gjennom arv har historisk sett ikke vært godt egnet til å håndtere en aktiv skrivestrøm eller et stort antall underordnede partisjoner. For eksempel kan du huske at algoritmen for å velge den nødvendige delen hadde kvadratisk kompleksitet, at det fungerer med 100+ seksjoner, forstår du selv hvordan...

I PG10 ble denne situasjonen sterkt optimalisert ved å implementere støtte innfødt partisjonering. Derfor prøvde vi umiddelbart å bruke den umiddelbart etter migrering av lagringen, men...

Som det viste seg etter å ha gravd gjennom manualen, er den opprinnelig partisjonerte tabellen i denne versjonen:

  • støtter ikke indeksbeskrivelser
  • støtter ikke triggere på den
  • kan ikke være noens "etterkommer"
  • ikke støtter INSERT ... ON CONFLICT
  • kan ikke generere en seksjon automatisk

Etter å ha fått et smertefullt slag i pannen med en rive, innså vi at det ville være umulig å gjøre uten å endre søknaden, og utsatte videre forskning i seks måneder.

PG10: andre sjanse

Så vi begynte å løse problemene som oppsto en etter en:

  1. Fordi utløser og ON CONFLICT Vi fant ut at vi fortsatt trengte dem her og der, så vi lagde et mellomstadium for å finne ut av dem proxy-tabell.
  2. Ble kvitt "ruting" i triggere - altså fra EXECUTE.
  3. De tok den ut separat maltabell med alle indekserslik at de ikke en gang er tilstede i proxy-tabellen.

Vi skriver i PostgreSQL på sublight: 1 vert, 1 dag, 1TB
Til slutt, etter alt dette, partisjonerte vi hovedtabellen naturlig. Opprettelsen av en ny seksjon er fortsatt overlatt til søknadens samvittighet.

"Saging" ordbøker

Som i ethvert analytisk system hadde vi også "fakta" og "kutt" (ordbøker). I vårt tilfelle handlet de i denne egenskapen f.eks. maltekst lignende trege spørringer eller teksten til selve spørringen.

"Fakta" ble seksjonert etter dag i lang tid allerede, så vi slettet rolig utdaterte seksjoner, og de plaget oss ikke (logger!). Men det var et problem med ordbøker...

Ikke for å si at det var mange av dem, men ca 100 TB med "fakta" resulterte i en 2.5 TB ordbok. Du kan ikke enkelt slette noe fra en slik tabell, du kan ikke komprimere den i tilstrekkelig tid, og det ble gradvis tregere å skrive til den.

Som en ordbok... i den skal hver oppføring presenteres nøyaktig én gang... og dette er riktig, men!.. Ingen hindrer oss i å ha en egen ordbok for hver dag! Ja, dette gir en viss redundans, men det tillater:

  • skrive/lese raskere på grunn av mindre seksjonsstørrelse
  • bruker mindre minne ved å jobbe med mer kompakte indekser
  • lagre mindre data på grunn av muligheten til raskt å fjerne utdaterte

Som et resultat av hele komplekset av tiltak CPU-belastningen ble redusert med ~30 %, diskbelastningen med ~50 %:

Vi skriver i PostgreSQL på sublight: 1 vert, 1 dag, 1TB
Samtidig fortsatte vi å skrive nøyaktig det samme inn i databasen, bare med mindre belastning.

#2. Databaseevolusjon og refaktorering

Så vi bestemte oss for det vi har hver dag har sin egen del med data. Faktisk, CHECK (dt = '2018-10-12'::date) — og det er en partisjoneringsnøkkel og betingelsen for at en post skal falle inn i en bestemt seksjon.

Siden alle rapporter i tjenesten vår er bygget i sammenheng med en bestemt dato, har indeksene for dem siden "ikke-partisjonerte tider" vært alle typer (Server, Dato, planmal), (Server, Dato, Plan node), (Dato, Feilklasse, Server), ...

Men nå bor de på hver seksjon dine kopier hver slik indeks... Og innenfor hver seksjon dato er en konstant... Det viser seg at nå er vi i hver slik indeks bare angi en konstant som et av feltene, som øker både volumet og søketiden for det, men ikke gir noe resultat. De overlot raken til seg selv, ups...

Vi skriver i PostgreSQL på sublight: 1 vert, 1 dag, 1TB
Optimaliseringsretningen er åpenbar – enkel fjern datofeltet fra alle indekser på partisjonerte tabeller. Gitt våre volumer er gevinsten ca 1TB/uke!

La oss nå merke oss at denne terabyten fortsatt måtte spilles inn på en eller annen måte. Det vil si, vi også disken skal nå laste mindre! Dette bildet viser tydelig effekten oppnådd fra rengjøringen, som vi viet en uke til:

Vi skriver i PostgreSQL på sublight: 1 vert, 1 dag, 1TB

#3. "Spredning" av toppbelastningen

En av de store problemene med lastede systemer er redundant synkronisering noen operasjoner som ikke krever det. Noen ganger "fordi de ikke la merke til det", noen ganger "det var lettere sånn", men før eller siden må du bli kvitt det.

La oss zoome inn på forrige bilde og se at vi har en disk "pumper" under belastningen med dobbel amplitude mellom tilstøtende prøver, noe som klart "statistisk" ikke bør skje med et slikt antall operasjoner:

Vi skriver i PostgreSQL på sublight: 1 vert, 1 dag, 1TB

Dette er ganske enkelt å få til. Vi har allerede begynt å overvåke nesten 1000 servere, hver behandles av en separat logisk tråd, og hver tråd tilbakestiller den akkumulerte informasjonen som skal sendes til databasen med en viss frekvens, noe som dette:

setInterval(sendToDB, interval)

Problemet her ligger nettopp i det faktum at alle tråder starter omtrent samtidig, så sendetidene deres faller nesten alltid sammen "til poenget." Oops #2...

Heldigvis er dette ganske enkelt å fikse, legge til en "tilfeldig" oppkjøring etter tid:

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

#4. Vi cacher det vi trenger

Det tredje tradisjonelle høybelastningsproblemet er ingen cache hvor han er kunne å være.

For eksempel gjorde vi det mulig å analysere i form av plannoder (alle disse Seq Scan on users), men tenker umiddelbart at de for det meste er de samme - de glemte det.

Nei, selvfølgelig, ingenting skrives til databasen igjen, dette kutter av utløseren med INSERT ... ON CONFLICT DO NOTHING. Men disse dataene når fortsatt databasen, og det er unødvendig les for å se etter konflikt må gjøre. Oops #3...

Forskjellen i antall poster sendt til databasen før/etter caching er aktivert er åpenbar:

Vi skriver i PostgreSQL på sublight: 1 vert, 1 dag, 1TB

Og dette er den medfølgende nedgangen i lagringsmengden:

Vi skriver i PostgreSQL på sublight: 1 vert, 1 dag, 1TB

Totalt

"Terabyte-per-dag" høres bare skummelt ut. Hvis du gjør alt riktig, så er dette bare 2^40 byte / 86400 sekunder = ~12.5 MB/sat selv stasjonære IDE-skruer holdt. 🙂

Men seriøst, selv med en tidoblet "skjevhet" av belastningen i løpet av dagen, kan du enkelt møte egenskapene til moderne SSD-er.

Vi skriver i PostgreSQL på sublight: 1 vert, 1 dag, 1TB

Kilde: www.habr.com

Legg til en kommentar