Pišemo u PostgreSQL na sublight: 1 host, 1 dan, 1TB

Nedavno sam vam rekao kako, koristeći standardne recepte povećati performanse SQL upita za čitanje iz PostgreSQL baze podataka. Danas ćemo pričati o tome kako snimanje može biti efikasnije u bazi podataka bez upotrebe ikakvih „okretanja“ u konfiguraciji - jednostavno pravilnom organizacijom tokova podataka.

Pišemo u PostgreSQL na sublight: 1 host, 1 dan, 1TB

#1. Sectioning

Članak o tome kako i zašto se isplati organizirati primijenjeno particioniranje "u teoriji" je već bilo, ovdje ćemo govoriti o praksi primjene nekih pristupa unutar našeg servis za praćenje za stotine PostgreSQL servera.

"Stvari prošlih dana..."

U početku, kao i svaki MVP, naš projekat je započeo pod prilično malim opterećenjem - praćenje je vršeno samo za deset najkritičnijih servera, sve tabele su bile relativno kompaktne... Ali kako je vreme odmicalo, broj nadgledanih hostova je postajao sve veći , i još jednom smo pokušali da uradimo nešto sa jednim od stolovi veličine 1.5TB, shvatili smo da je, iako je moguće nastaviti ovako živjeti, bilo vrlo nezgodno.

Vremena su bila skoro kao epska vremena, različite verzije PostgreSQL 9.x su bile relevantne, tako da je sve particioniranje moralo biti obavljeno “ručno” - preko nasljeđivanje tablice i okidači rutiranje sa dinamikom EXECUTE.

Pišemo u PostgreSQL na sublight: 1 host, 1 dan, 1TB
Dobiveno rješenje pokazalo se dovoljno univerzalnim da se može prevesti na sve tablice:

  • Deklarisana je prazna roditeljska tabela “header”, koja je sve opisala potrebne indekse i pokretače.
  • Zapis sa stanovišta klijenta napravljen je u „root“ tabeli i interno korišćenjem okidač za rutiranje BEFORE INSERT zapis je "fizički" ubačen u traženi odjeljak. Ako toga još nije bilo, uhvatili smo izuzetak i...
  • … korištenjem CREATE TABLE ... (LIKE ... INCLUDING ...) kreiran je na osnovu šablona roditeljske tabele odjeljak sa ograničenjem na željeni datumtako da se prilikom preuzimanja podataka čitanje vrši samo u njima.

PG10: prvi pokušaj

Ali particioniranje putem nasljeđivanja istorijski nije bilo dobro za rad s aktivnim tokom pisanja ili velikim brojem podređenih particija. Na primjer, možete se sjetiti da je algoritam za odabir traženog odjeljka imao kvadratna složenost, da radi sa 100+ sekcija, sami razumete kako...

U PG10 ova situacija je uvelike optimizirana implementacijom podrške izvorno particioniranje. Stoga smo ga odmah pokušali primijeniti odmah nakon migracije skladišta, ali...

Kako se pokazalo nakon kopanja po priručniku, izvorno particionirana tablica u ovoj verziji je:

  • ne podržava opise indeksa
  • ne podržava okidače na njemu
  • ne može biti ničiji "potomak"
  • ne podržavaju INSERT ... ON CONFLICT
  • ne može automatski generirati odjeljak

Zadobivši bolan udarac grabljama u čelo, shvatili smo da je nemoguće bez modifikacije aplikacije i odgodili smo daljnje istraživanje za šest mjeseci.

PG10: druga šansa

Dakle, počeli smo rješavati probleme koji su se pojavili jedan po jedan:

  1. Jer okidači i ON CONFLICT Otkrili smo da su nam tu i tamo još potrebni, pa smo napravili međufazu da ih razradimo proxy table.
  2. Riješio se "usmjeravanja" u okidačima - odnosno od EXECUTE.
  3. Izvadili su ga odvojeno tabela šablona sa svim indeksimatako da ih nema ni u proxy tabeli.

Pišemo u PostgreSQL na sublight: 1 host, 1 dan, 1TB
Konačno, nakon svega ovoga, matično smo podijelili glavnu tablicu. Stvaranje novog odjeljka je još uvijek ostavljeno na savjesti aplikacije.

Rečnici „testerisanja“.

Kao iu svakom analitičkom sistemu, i mi smo imali "činjenice" i "rezovi" (rječnici). U našem slučaju, u tom svojstvu su djelovali npr. tijelo šablona slični spori upiti ili tekst samog upita.

“Činjenice” su se već duže vrijeme dijelile po danima, pa smo mirno brisali zastarjele rubrike, a nisu nam smetale (logovi!). Ali postojao je problem sa rječnicima...

Da ne kažem da ih je bilo puno, ali otprilike 100TB “činjenica” rezultiralo je rječnikom od 2.5TB. Ne možete prikladno izbrisati ništa iz takve tablice, ne možete je komprimirati u odgovarajućem vremenu, a pisanje u nju je postepeno postajalo sporije.

Kao rečnik...u njemu svaki unos treba da bude predstavljen tačno jednom...i to je tačno, ali!.. Niko nam ne brani da imamo poseban rečnik za svaki dan! Da, ovo donosi određenu redundantnost, ali omogućava:

  • brže pisati/čitati zbog manje veličine presjeka
  • troše manje memorije radom sa kompaktnijim indeksima
  • pohraniti manje podataka zbog mogućnosti brzog uklanjanja zastarjelih

Kao rezultat čitavog kompleksa mjera Opterećenje CPU-a smanjeno za ~30%, opterećenje diska za ~50%:

Pišemo u PostgreSQL na sublight: 1 host, 1 dan, 1TB
Istovremeno, nastavili smo da upisujemo potpuno istu stvar u bazu podataka, samo sa manjim opterećenjem.

#2. Evolucija baze podataka i refaktoring

Tako da smo se odlučili na ono što imamo svaki dan ima svoj dio sa podacima. zapravo, CHECK (dt = '2018-10-12'::date) — i postoji ključ za particionisanje i uslov da zapis padne u određeni odeljak.

Budući da su svi izvještaji u našem servisu izgrađeni u kontekstu određenog datuma, indeksi za njih od “neparticioniranih vremena” su svih vrsta (Server, Datum, predložak plana), (Server, Datum, Planski čvor), (Datum, klasa greške, server)...

Ali sada žive na svakom dijelu vaše kopije svaki takav indeks... I unutar svake sekcije datum je konstanta... Ispada da smo sada u svakom takvom indeksu jednostavno unesite konstantu kao jedno od polja, koje povećava i njegov obim i vrijeme pretrage, ali ne donosi nikakav rezultat. Grablje su ostavili sami sebi, up...

Pišemo u PostgreSQL na sublight: 1 host, 1 dan, 1TB
Smjer optimizacije je očigledan – jednostavan uklonite polje datuma iz svih indeksa na podijeljenim stolovima. S obzirom na naše količine, dobitak je otprilike 1TB/sedmično!

Napominjemo da je ovaj terabajt ipak morao nekako biti snimljen. Odnosno, i mi disk bi se sada trebao manje učitavati! Ova slika jasno pokazuje efekat koji se dobija od čišćenja, kojem smo posvetili nedelju dana:

Pišemo u PostgreSQL na sublight: 1 host, 1 dan, 1TB

#3. „Širenje“ vršnog opterećenja

Jedan od velikih problema opterećenih sistema je redundantna sinhronizacija neke operacije koje to ne zahtijevaju. Nekad „zato što nisu primetili“, nekada „tako je bilo lakše“, ali pre ili kasnije toga se morate rešiti.

Hajde da uvećamo prethodnu sliku i vidimo da imamo disk “pumpe” pod opterećenjem sa dvostrukom amplitudom između susjednih uzoraka, što se očito "statistički" ne bi smjelo dogoditi s ovolikim brojem operacija:

Pišemo u PostgreSQL na sublight: 1 host, 1 dan, 1TB

Ovo je prilično lako postići. Već smo počeli sa praćenjem skoro 1000 servera, svaki se obrađuje od strane zasebne logičke niti, a svaka nit resetuje akumulirane informacije koje se šalju u bazu podataka na određenoj frekvenciji, otprilike ovako:

setInterval(sendToDB, interval)

Problem ovdje leži upravo u tome što sve niti počinju otprilike u isto vrijeme, pa se njihova vremena slanja gotovo uvijek poklapaju "do tačke". Ups #2...

Srećom, ovo je prilično lako popraviti, dodavanje "slučajnog" zaleta po vremenu:

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

#4. Keširamo ono što nam treba

Treći tradicionalni problem visokog opterećenja je nema keš memorije gde je on mogao biti.

Na primjer, omogućili smo analizu u smislu čvorova plana (sve ove Seq Scan on users), ali odmah pomislite da su, uglavnom, isti - zaboravili su.

Ne, naravno, ništa se ponovo ne upisuje u bazu podataka, ovo prekida okidač sa INSERT ... ON CONFLICT DO NOTHING. Ali ovi podaci i dalje dospiju u bazu podataka i nisu potrebni čitanje radi provjere sukoba moram uraditi. Ups #3...

Razlika u broju zapisa poslatih u bazu podataka prije/nakon što je omogućeno keširanje je očigledna:

Pišemo u PostgreSQL na sublight: 1 host, 1 dan, 1TB

A ovo je prateći pad opterećenja skladišta:

Pišemo u PostgreSQL na sublight: 1 host, 1 dan, 1TB

Ukupno

“Terabajt po danu” jednostavno zvuči zastrašujuće. Ako sve uradite kako treba, onda je ovo pravedno 2^40 bajtova / 86400 sekundi = ~12.5MB/sda čak i desktop IDE šrafovi drže. 🙂

Ali ozbiljno, čak i sa desetostrukim „iskrivljenjem“ opterećenja tokom dana, lako možete zadovoljiti mogućnosti modernih SSD-ova.

Pišemo u PostgreSQL na sublight: 1 host, 1 dan, 1TB

izvor: www.habr.com

Dodajte komentar