Shkruajmë në PostgreSQL në sublumin: 1 host, 1 ditë, 1TB

Së fundi tregova se si me anë të recetave standarde mund të rritet performanca e SQL-ve të 'leximit' nga baza e të dhënave PostgreSQL. Sot do të flasim se si mund të bëhet më efikas shkrimi në DB pa përdorur ndonjë 'përpunues' në konfigurim — thjesht duke organizuar siç duhet rrjedhat e të dhënave.

Shkruajmë në PostgreSQL në sublumin: 1 host, 1 ditë, 1TB

#1. Секционирование

Artikulli për atë se si dhe pse duhet të organizohet sekcionimi i aplikacioneve 'në teori' ka qenë, tani do të flasim për praktikën e zbatimit të disa qasjeve në kuadër të shërbimit tonë të monitorimit të qindra serverëve PostgreSQL.

‘Kohët e kaluara…’

Fillimisht, si çdo MVP tjetër, projekti ynë filloi me një ngarkesë mjaft të vogël — monitorimi bëhej vetëm për disa nga serverët më kritikë, të gjitha tabelat ishin relativisht kompakte… Por koha kalonte, numri i hosteve të monitoruar po rritej vazhdimisht, dhe duke u përpjekur përsëri të bënim diçka me një nga tabelat me masë 1.5TB, kuptuam se duke jetuar kështu edhe mundeshim, por ishte shumë e papështatshme.

Koha ishte pothuajse legjendare, versionet e ndryshme të PostgreSQL 9.x ishin aktuale, prandaj çdo sekcionim duhej të bëhej 'me duar' — përmes Trashëgimisë së tabelave dhe triggerave të routingut me dinamikë. EKZEKUTOJ.

Shkruajmë në PostgreSQL në sublumin: 1 host, 1 ditë, 1TB
Zgjidhja e arritur rezultoi të ishte mjaft universale, që mund të transponoheshin në të gjitha tabelat:

  • U shpall një tabelë 'prind' bosh, në të cilën u përshkruan të gjitha indekset dhe triggerat e nevojshëm..
  • Shkrimi nga këndvështrimi i klientit bëhej në tabelën 'rrënjë', dhe brenda saj me anë të triggerit të routingut BEFORE INSERT shkrimi 'fizikisht' vendosej në seksionin e duhur. Nëse një e tillë nuk kishte ekzistuar akoma – ne kapnim përjashtimin dhe …
  • … me anë të CREATE TABLE ... (LIKE ... INCLUDING ...) sipërfaqeve të tabelës prind krijohej seksioni me kufizimin në datën e nevojshme, që gjatë nxjerrjes së të dhënave lexohej vetëm në të.

PG10: përpjekja e parë

Por sekcionimi përmes trashëgimisë historikisht nuk ishte shumë i përshtatshëm për t'u punuar me një rrjedhë aktive shkrimi ose me një numër të madh seksionesh-përfituese. Për shembull, mund të kujtojmë se algoritmi për zgjedhjen e seksionit të nevojshëm kishte kompleksitetin katror, që me mbi 100 seksione punon, e kuptoni si …

Në PG10 kjo situatë u optimizua shumë, duke realizuar mbështetje për sekcionimin e natyrshëm.Prandaj e provuam menjëherë pas migrimit të depozitës, por …

Siç u zbulua pas shqyrtimit të manualit, tabela e sekcionuar në këtë version:

  • nuk mbështet përshkrimin e indekseve
  • nuk mbështet triggerat mbi të
  • nuk mund të jetë vetë pasardhëse e askujt
  • nuk mbështet INSERT ... ON CONFLICT
  • nuk di të gjenerojë seksione automatikisht

Pas një goditjeje në ballë me grushta, kuptuam se pa modifikimin e aplikacionit nuk do të mundeshim, dhe e shtymë studimin e mëtejmë për gjashtë muaj.

PG10: shansi i dytë

Pra, filluam të zgjidhim problemet e shfaqura një nga një:

  1. Duke qenë se triggerat dhe ON CONFLICT na ishin të nevojshme disa herë, për ta trajtuar bëmë një tabelë ndërmjetëse..
  2. Shkëputëm 'routingun' në triggera — dmth EKZEKUTOJ.
  3. Dijmë veçmas tabelën-shembull me të gjitha indekset, në mënyrë që ato të mos ishin të pranishme as në tabelën ndërmjetëse.

Shkruajmë në PostgreSQL në sublumin: 1 host, 1 ditë, 1TB
Në fund, pas gjithë kësaj, sekcionuam tabelën kryesore në mënyrë natyrale. Krijimi i një seksioni të ri mbeti për të qenë në duar të aplikacionit.

'Dizajnojmë' fjalorët

Si në çdo sistem analitik, edhe ne kishim 'fakte' dhe 'dimensione' (fjalorë). Në rastin tonë, ky rol e kishin, për shembull, trupi i 'shembullit' të pyetjeve të ngadalta ose vetë teksti i pyetjes.

'Faktet' tona ishin të sekcionuara në ditë qysh shumë kohë më parë, aq sa i hiqnim me lehtësi seksionet e vjetruara dhe ato nuk na shqetësonin (për shkak të logeve!). Por me fjalorët ndodhi ndryshe…

Nuk do të thosha se kishte shumë prej tyre, por për çdo 100TB 'faktesh' dolën një fjalor prej 2.5TB. Nga një tabelë e tillë nuk mund të hiqet asgjë lehtësisht, nuk mund të kompresohet brenda një kohe të arsyeshme, dhe gjithashtu, shkrimi brenda saj po bëhej gjithnjë e më e ngadaltë.Duket si fjalor… çdo regjistër duhet të paraqitet saktësisht një herë… dhe kjo është e saktë, por!.. Askush nuk na ndalon të kemi

një fjalor të veçantë për çdo ditë ! Po, kjo sjell një sasi të caktuar të tepërt, por gjithashtu lejon:të shkruajmë/lehtësojmë më shpejt

  • në sajë të masës më të vogël të seksionit përdor më pak memorie
  • në sajë të punës me indekset më kompakte të ruajmë më pak të dhëna
  • në sajë të zgjedhjes për të hequr shpejt ato të vjetruara. Si rezultat i gjithë kompleksit të aktiviteteve

ngarkesa në CPU u zvogëlua me rreth 30%, në disk — me rreth 50% Dhe ne vazhduam të shkruajmë në bazën e të dhënave të njëjtë, vetëm me një ngarkesë më të vogël.:

Shkruajmë në PostgreSQL në sublumin: 1 host, 1 ditë, 1TB
Megjithatë, vazhduam të shkruajmë në bazë të njëjtit informacion, thjesht me një ngarkesë më të vogël.

#2. Эволюция и рефакторинг БД

Pra, ne arritëm të kuptojmë se kemi një seksion për çdo ditë, duke e bërë atë. CHECK (dt = '2018-10-12'::date) — dhe ky është çelësi i sekcionimit dhe kushti për të futur një regjistër në seksionin përkatës.

Duke qenë se të gjitha raportet në shërbimin tonë janë ndarë sipas datave specifike, indikatoret e tyre që erdhën që nga koha 'para sekcionimit' kishin të gjithë formën e (Server, Data, Shabllon plani), (Server, Data, Nodi i planit), (Data, Klasa e gabimeve, Server),…

Por tani, në çdo seksion jetojnë ekzemplarët e vet të çdo indikatori të tillë… Dhe në kuadër të çdo seksioni data është një konstantë… Pra, tani tani ne e futim këtë konstantë si një nga fushat, që përmirëson dhe volumet e tij dhe kohën e kërkimit, por nuk sjell rezultat. Ne kemi lënë një 'kurth' për veten, ups… si një nga fushat, duke rritur si vëllimin ashtu edhe kohën e kërkimit mbi të, por pa sjellë ndonjë rezultat. Vetë i lanë gabimet, ups…

Shkruajmë në PostgreSQL në sublumin: 1 host, 1 ditë, 1TB
Drejtimi i optimizimit është i qartë — thjesht heqim fushën e datës nga të gjitha indeksat në tabelat e sekcionuara. Me volumin tonë, fitimi është rreth 1TB/në javë!

Dhe tani le të vëmë re se ky terabajt duhet gjithashtu të regjistrohet diku. Kështu që tani ne duhet të ngarkojmë më pak disk! Në këtë imazh, efekti i arritur nga pastrimi i bërë, për të cilin përkushtuam një javë, është shumë i dukshëm:

Shkruajmë në PostgreSQL në sublumin: 1 host, 1 ditë, 1TB

#3. «Размазываем» пиковую нагрузку

Një nga fatkeqësitë e mëdha të sistemeve të ngarkuara është sinkronizimi i tepruar i disa operacioneve që nuk e kërkojnë atë. Ndonjëherë 'sepse nuk e vëmë re', ndonjëherë 'ishte më e lehtë', por herët ose vonë duhet të heqim dorë prej saj.

Afrojmë imazhin e mëparshëm — dhe shohim se disku ynë ‘ngarkon’ me dy herë amplitudën në mes të mostra, që nuk duhet të ndodhte 'statistikisht' me një numër të tillë operacionesh:

Shkruajmë në PostgreSQL në sublumin: 1 host, 1 ditë, 1TB

Ta arrijmë këtë është mjaft e thjeshtë. Që kemi monitoruar pothuajse 1000 serverë,çdo njëri përpunon një rrjedhë logjike të veçantë, dhe çdo rrjedhë derdh informacionin e akumuluar për ta dërguar në bazën e të dhënave me një periudhë të caktuar, përafërsisht kështu:

setInterval(sendToDB, interval)

Problemi këtu qëndron pikërisht në faktin se të gjitha rrjedhët fillojnë përafërsisht në të njëjtën kohë, kështu që momentet e dërgimit janë pothuajse gjithmonë të njëjta 'deri në pikë'. Ups nr.2…

Me fat, kjo rregullohet mjaft lehtë, duke shtuar një ‘në rast’ hapësire sipër gjatë kohës:

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

#4. Кэшируем, что нужно можно

Problemi i tretë tradicional i ngarkesës së lartë — mungesa e caches aty ku ai mund të duhet të jetë.

Për shembull, ne krijuam mundësinë e analizës sipas nodave të planit (të gjitha këto Seq Scan on users), por menjëherë e humbëm idenë se ato, në shumicën, janë të njëjta.

Jo, sigurisht, nuk ka asnjë shkrim të përsëritur në bazë, kjo e kthen triggerin me INSERT ... ON CONFLICT DO NOTHING. Por këto të dhëna gjithsesi arrijnë në bazë, dhe akoma është një lexim të tepruar për të kontrolluar konfliktin duhet të bëjmë. Ups nr.3…

Dallimi në numrin e regjistrave të dërguar në bazë para/pas aktivizimit të caches është i qartë:

Shkruajmë në PostgreSQL në sublumin: 1 host, 1 ditë, 1TB

Dhe kjo është rënia e ngarkesës në depo:

Shkruajmë në PostgreSQL në sublumin: 1 host, 1 ditë, 1TB

Në përfundim

‘Terabajt në ditë’ vetëm duket e frikshme. Nëse gjithçka bëni në mënyrë të saktë, atëherë kjo është vetëm 2^40 byte / 86400 sekonda = ~12.5MB/s, që e mbajtën madje edhe disqet IDE të tavolinës. 🙂

Por nëse flasim seriozisht, madje edhe në një ‘stres’ dhjetëfish të ngarkesës gjatë ditës, mund të ankoroheni rehat në mundësitë e SSD-ve moderne.

Shkruajmë në PostgreSQL në sublumin: 1 host, 1 ditë, 1TB

Burimi: habr.com

Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS 🔥 Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS | ProHoster