Skriver i PostgreSQL pÄ subljus: 1 vÀrd, 1 dag, 1TB

Nyligen berĂ€ttade jag hur man med hjĂ€lp av standardrecept kan öka prestandan för SQL-frĂ„gor "för lĂ€sning" frĂ„n PostgreSQL-databasen. Idag handlar det om hur man kan göra skrivning till databasen mer effektiv utan att anvĂ€nda nĂ„gra "justeringar" i konfigurationen – helt enkelt genom att organisera datastreams pĂ„ rĂ€tt sĂ€tt.

Skriver i PostgreSQL pÄ subljus: 1 vÀrd, 1 dag, 1TB

#1. ĐĄĐ”ĐșŃ†ĐžĐŸĐœĐžŃ€ĐŸĐČĐ°ĐœĐžĐ”

En artikel om hur och varför man bör organisera applikationsseparation "i teorin" har redan funnits, hÀr handlar det om praktisk tillÀmpning av vissa metoder inom ramen för vÄr övervakningstjÀnst för hundratals PostgreSQL-servrar.

"Det var lÀnge sen
"

Till en början, precis som varje MVP, startade vĂ„rt projekt under en ganska lĂ„g belastning – övervakningen skedde endast för ett dussin av de mest kritiska servrarna, alla tabeller var relativt kompakta
 Men tiden gick och antalet övervakade vĂ€rdar ökade, och nĂ€r vi försökte göra nĂ„got med en av tabellerna som var 1,5 TB, insĂ„g vi att det visserligen gick att leva sĂ„, men det var mycket obekvĂ€mt.

Tiderna var nĂ€stan legendariska och olika versioner av PostgreSQL 9.x var aktuella, sĂ„ all separation fick göras "manuellt" – genom tabellĂ€rv och triggar routing med dynamiska KÖR.

Skriver i PostgreSQL pÄ subljus: 1 vÀrd, 1 dag, 1TB
Den lösning som uppstod visade sig vara tillrÀckligt universell för att kunna överföras till alla tabeller:

  • En tom "huvud"-förĂ€ldratabell deklarerades, dĂ€r alla nödvĂ€ndiga index och triggar beskrevs.
  • Skrivning frĂ„n klientens perspektiv gjordes i "rot"-tabellen, och inuti med hjĂ€lp av ruttrigger BEFORE INSERT gjordes den "fysiskt" i rĂ€tt sektion. Om den inte redan fanns fĂ„ngade vi upp ett undantag och 

  • 
 med hjĂ€lp av CREATE TABLE ... (LIKE ... INCLUDING ...) skapades en sektion med begrĂ€nsning pĂ„ rĂ€tt datum, sĂ„ att nĂ€r data hĂ€mtades gjordes lĂ€sningen endast i den.

PG10: första försöket

Men sektionering genom arv har historiskt sett inte varit sĂ€rskilt anpassad för att hantera en aktiv skrivström eller ett stort antal underordnade sektioner. Till exempel kan man minnas att algoritmen för att vĂ€lja rĂ€tt sektion hade kvadratisk komplexitet, vilket, med 100+ sektioner, fungerar, ja ni förstĂ„r ju hur


I PG10 har denna situation optimerats kraftigt, med stöd för inbyggd sektionering. DĂ€rför försökte vi direkt anvĂ€nda det efter migreringen av lagringen, men


NÀr vi grÀvde igenom manualen visade det sig att den nativt partitionerade tabellen i denna version:

  • inte stödjer beskrivning av index
  • inte stöder triggers pĂ„ den
  • kan inte vara ingen "avkomling"
  • stödjer inte INSERT ... ON CONFLICT
  • kan inte automatiskt generera partition

Efter att ha fÄtt en smÀll i pannan med en kratta insÄg vi att vi inte skulle kunna klara oss utan att modifiera applikationen, och vi sköt upp vidare forskning i sex mÄnader.

PG10: en andra chans

SÄ vi började lösa de uppkomna problemen ett i taget:

  1. Eftersom triggers och ON CONFLICT visade sig vara nödvÀndiga pÄ vissa stÀllen, för att hantera dem skapade vi en mellan- proxy-tabell.
  2. Vi blev av med "routing" i triggers — det vill sĂ€ga av KÖR.
  3. Vi separerade ut en malltabell med alla index, sÄ att de inte ens skulle finnas i proxy-tabellen.

Skriver i PostgreSQL pÄ subljus: 1 vÀrd, 1 dag, 1TB
Till slut, efter allt detta, partitionerade vi den huvudsakliga tabellen nativt. Att skapa en ny partition blev kvar pÄ applikationens samvete.

"Vi skapar" ordböcker

Precis som i varje analytiskt system hade vi ocksÄ "fakta" och "dimensioner" (ordböcker). I vÄrt fall var detta till exempel kroppen av "mallen" av typiska lÄngsamma förfrÄgningar eller sjÀlva texten av förfrÄgningen.

"Fakta" hade vi redan partitionerat efter dagar ganska lĂ€nge, sĂ„ vi tog lugnt bort förĂ„ldrade partitioner, och de störde oss inte (loggarna alltsĂ„!). Men med ordböckerna blev det problem


Inte för att det fanns mycket av dem, men ungefÀr för 100TB "fakta" fick vi ett ordbok pÄ 2,5TB.. FrÄn en sÄdan tabell Àr det bekvÀmt att inte ta bort nÄgot, inte komprimera pÄ rimlig tid, och skrivningen blev gradvis allt lÄngsammare.

SĂ€rskilt ordboken
 varje post bör representeras exakt en gĂ„ng
 och det Ă€r korrekt, men!.. Ingen hindrar oss frĂ„n att ha en separat ordbok för varje dag! Ja, det ger viss överflöd, men det möjliggör:

  • att skriva/lĂ€sa snabbare tack vare mindre storlek pĂ„ partitionen
  • att anvĂ€nda mindre minne tack vare arbete med mer kompakta index
  • att lagra mindre data tack vare möjligheten till snabb radering av förĂ„ldrade

Som ett resultat av hela ÄtgÀrdskomplexet minskade CPU-belastningen med ~30%, diskbelastningen med ~50%:

Skriver i PostgreSQL pÄ subljus: 1 vÀrd, 1 dag, 1TB
Under tiden fortsatte vi att skriva till databasen exakt detsamma, bara med mindre belastning.

#2. Đ­ĐČĐŸĐ»ŃŽŃ†ĐžŃ Đž рДфаĐșŃ‚ĐŸŃ€ĐžĐœĐł БД

SĂ„ vi stannade vid att vi har en sektion för varje dag med data. Faktiskt, CHECK (dt = '2018-10-12'::date) — Ă€r nyckeln för sektioneringen och villkoret för att en post ska hamna i en specifik sektion.

Eftersom alla rapporter i vÄr tjÀnst byggs upp i samband med ett specifikt datum, sÄ hade ocksÄ indexen frÄn "icke-sektionerade tider" alltid varit av typen (Server, Datum, Planmall), (Server, Datum, Plannod), (Datum, Felklass, Server),


Men nu finns det egna instanser av varje sĂ„dant index... Och inom varje sektion Ă€r datumet en konstant
 SĂ„ nu skriver vi bokstavligen in konstanten som ett av fĂ€lten i varje sĂ„dant index, vilket gör bĂ„de dess volym och söktiden större, men utan att ge nĂ„got resultat. Vi har lĂ€mnat oss sjĂ€lva fĂ€llor, oops... Riktningen för optimeringen Ă€r uppenbar — helt enkelt

Skriver i PostgreSQL pÄ subljus: 1 vÀrd, 1 dag, 1TB
tar vi bort fÀltet med datum frÄn alla index i sektionerade tabeller. Med vÄra volymer ger det en vinst pÄ omkring 1TB/vecka Och nu lÄt oss notera att denna terabyte ocksÄ mÄste registreras pÄ nÄgot sÀtt. Det vill sÀga vi mÄste nu ocksÄ!

belasta disken mindre ! Denna bild visar tydligt effekten av den stÀdning vi Àgnade en vecka Ät:Ett av de stora problemen med belastade system Àr

Skriver i PostgreSQL pÄ subljus: 1 vÀrd, 1 dag, 1TB

#3. Â«Đ Đ°Đ·ĐŒĐ°Đ·Ń‹ĐČĐ°Đ”ĐŒÂ» пОĐșĐŸĐČую ĐœĐ°ĐłŃ€ŃƒĐ·Đșу

överskottssynkronisering av vissa operationer som inte krĂ€ver det. Ibland "för att man inte mĂ€rkte", ibland "för att det var enklare", men förr eller senare mĂ„ste man göra sig av med den. Vi zoomar in pĂ„ den tidigare bilden — och ser att disken

"drar" i belastning med dubbla amplituden mellan nÀrliggande mÀtningar, vilket uppenbarligen "statistiskt" inte borde hÀnda med sÄ mycket operationer: Det Àr ganska enkelt att uppnÄ. Vi hade redan

Skriver i PostgreSQL pÄ subljus: 1 vÀrd, 1 dag, 1TB

nÀstan 1000 servrar , var och en hanteras av en separat logisk ström, och varje ström skickar den insamlade informationen för att skickas till databasen med en viss periodicitet, ungefÀr sÄ hÀr:setInterval(sendToDB, interval)

Problemet hÀr ligger precis i det att

alla strömmar startar nÀstan samtidigt , sÄ tidpunkterna för att skicka sammanfaller nÀstan alltid "till punkt". Oops nr 2...Lyckligtvis kan detta fixas ganska enkelt,

genom att lÀgga till en "slumptalsbaserad" förskjutning i tid: setInterval(sendToDB, interval * (1 + 0.1 * (Math.random() - 0.5)))

Det tredje traditionella highload-problemet —

#4. ĐšŃŃˆĐžŃ€ŃƒĐ”ĐŒ, Ń‡Ń‚ĐŸ ĐœŃƒĐ¶ĐœĐŸ ĐŒĐŸĐ¶ĐœĐŸ

Ă€r frĂ„nvaron av cache dĂ€r den borde vara. skulle kunna Đ±Ń‹Ń‚ŃŒ.

Till exempel har vi möjliggjort analys pĂ„ planens nodnivĂ„ (alla dessa Seq Scan pĂ„ anvĂ€ndare), men att direkt tĂ€nka att de, i huvudsak, Ă€r identiska — Ă€r glömt.

Nej, sjĂ€lvklart skrivs inget upprepade gĂ„nger till databasen, det blockerar triggern med INSERT ... ON CONFLICT DO NOTHING. Men dessa data nĂ„r fortfarande databasen, och dessutom krĂ€vs det onödig lĂ€sning för att kontrollera konflikten mĂ„ste göras. Oops nr 3


Skillnaden i mĂ€ngden poster som skickas till databasen före/efter aktivering av cachning — Ă€r uppenbar:

Skriver i PostgreSQL pÄ subljus: 1 vÀrd, 1 dag, 1TB

Och detta — Ă€r det tillhörande minskningen av belastningen pĂ„ lagringen:

Skriver i PostgreSQL pÄ subljus: 1 vÀrd, 1 dag, 1TB

Sammanfattningsvis

«Terabyte-per-dag» lĂ„ter bara skrĂ€mmande. Om du gör allt rĂ€tt, sĂ„ handlar det bara om 2^40 byte / 86400 sekunder = ~12.5MB/s, vilket Ă€ven konsumerades av stationĂ€ra IDE-hĂ„rddiskar. 🙂

Och om vi ska vara seriösa, sÄ kan du Àven med en tiodubblad belastning under ett dygn, lÀtt klara av kapaciteten hos moderna SSD:er.

Skriver i PostgreSQL pÄ subljus: 1 vÀrd, 1 dag, 1TB

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster