Wy skriuwe yn PostgreSQL op sublight: 1 host, 1 dei, 1TB

Koartlyn haw ik jo ferteld hoe, mei help fan standert resepten fergrutsje de prestaasjes fan SQL lêzen queries út PostgreSQL-database. Hjoed sille wy prate oer hoe opname kin dien wurde effisjinter yn 'e databank sûnder "twists" yn 'e konfiguraasje te brûken - gewoan troch de gegevensstreamen korrekt te organisearjen.

Wy skriuwe yn PostgreSQL op sublight: 1 host, 1 dei, 1TB

#1. Seksje

In artikel oer hoe en wêrom it wurdich is te organisearjen tapaste partisjonearring "yn teory" hat al west, hjir sille wy prate oer de praktyk fan it tapassen fan guon oanpakken binnen ús tafersjochtsjinst foar hûnderten PostgreSQL-tsjinners.

"Dingen fan ferline dagen ..."

Yn earste ynstânsje, lykas elke MVP, begon ús projekt ûnder in frij lichte lading - tafersjoch waard allinich útfierd foar de tsien meast krityske servers, alle tabellen wiene relatyf kompakt ... Mar as de tiid gie, waard it oantal kontroleare hosts mear en mear , en nochris wy besocht te dwaan wat mei ien fan tables 1.5TB yn grutte, wy realisearre dat hoewol't it mooglik wie om fierder te libjen sa, it wie hiel ûngemaklik.

De tiden wiene hast as epyske tiden, ferskate ferzjes fan PostgreSQL 9.x wiene relevant, sadat alle partitionearring "hânmjittich" dien wurde moast - fia tabel erfskip en triggers routing mei dynamyske EXECUTE.

Wy skriuwe yn PostgreSQL op sublight: 1 host, 1 dei, 1TB
De resultearjende oplossing die bliken universeel genôch te wêzen dat it koe wurde oerset nei alle tabellen:

  • In lege "header" âlder tabel waard ferklearre, dy't beskreaun alles needsaaklike yndeksen en triggers.
  • It rekord út it eachpunt fan 'e kliïnt waard makke yn' e "root" tabel, en yntern mei help fan routing trigger BEFORE INSERT it rekord waard "fysyk" ynfoege yn de fereaske seksje. As d'r noch net wie, hawwe wy in útsûndering fongen en ...
  • … troch te brûken CREATE TABLE ... (LIKE ... INCLUDING ...) waard makke op basis fan it sjabloan fan 'e âldertabel seksje mei in beheining op de winske datumsadat as gegevens wurde ophelle, it lêzen wurdt útfierd allinnich yn it.

PG10: earste besykjen

Mar ôfskieding troch erfenis hat histoarysk net goed geskikt west foar it omgean mei in aktive skriuwstream of in grut tal bernepartysjes. Bygelyks, kinne jo ûnthâlde dat it algoritme foar it selektearjen fan de fereaske seksje hie kwadratyske kompleksiteit, dat it wurket mei 100+ seksjes, jo sels begripe hoe ...

Yn PG10 waard dizze situaasje sterk optimalisearre troch it útfieren fan stipe native partitioning. Dêrom hawwe wy fuortendaliks besocht it direkt nei it migrearjen fan de opslach oan te passen, mar ...

As it die bliken nei it graven troch de hantlieding, is de native partitioned tabel yn dizze ferzje:

  • stipet gjin yndeksbeskriuwings
  • stipet gjin triggers derop
  • kin gjin "ôfstammeling" fan ien wêze
  • net stypje INSERT ... ON CONFLICT
  • kin gjin seksje automatysk generearje

Nei't wy in pynlike klap op 'e foarholle krigen hawwe mei in hark, realisearre wy dat it ûnmooglik wêze soe sûnder de oanfraach te feroarjen, en hawwe fierder ûndersyk seis moanne útsteld.

PG10: twadde kâns

Sa, wy begûn te lossen de problemen dy't ûntstienen ien foar ien:

  1. Omdat triggers en ON CONFLICT Wy fûnen dat wy se hjir en dêr noch nedich hiene, dus makken wy in tuskenstap om se út te wurkjen proxy tabel.
  2. Ferwiderje fan "routing" yn triggers - dat is, fan EXECUTE.
  3. Se namen it apart út template tabel mei alle yndeksensadat se net iens oanwêzich binne yn 'e proxytabel.

Wy skriuwe yn PostgreSQL op sublight: 1 host, 1 dei, 1TB
Uteinlik, nei dit alles, hawwe wy de haadtabel native partitioneare. It oanmeitsjen fan in nije seksje wurdt noch oerlitten oan it gewisse fan 'e applikaasje.

"Sawing" wurdboeken

Lykas yn elk analytysk systeem, hienen wy ek "feiten" en "besunigingen" (wurdboeken). Yn ús gefal hannelen se yn dizze hoedanichheid bgl. template lichem ferlykbere trage queries of de tekst fan 'e query sels.

"Feiten" waarden al in lange tiid troch de dei yndield, dus wy hawwe rêstich ferâldere seksjes wiske, en se hindere ús net (logs!). Mar der wie in probleem mei wurdboeken...

Net te sizzen dat der in protte wiene, mar sawat 100TB oan "feiten" resultearre yn in 2.5TB wurdboek. Jo kinne neat fan sa'n tabel maklik wiskje, jo kinne it net yn adekwate tiid komprimearje, en it skriuwen nei it waard stadichoan stadiger.

Lykas in wurdboek... dêryn moat elke yngong presys ien kear presintearre wurde... en dat is krekt, mar!.. Gjinien hâldt ús fan in apart wurdboek foar elke dei! Ja, dit bringt in bepaalde redundânsje, mar it lit:

  • skriuw / lêze flugger fanwege lytsere seksje grutte
  • konsumearje minder ûnthâld troch te wurkjen mei kompakter yndeksen
  • bewarje minder gegevens fanwegen de mooglikheid om fluch fuortsmite ferâldere

As gefolch fan it hiele kompleks fan maatregels CPU-load fermindere mei ~30%, skiifladen mei ~50%:

Wy skriuwe yn PostgreSQL op sublight: 1 host, 1 dei, 1TB
Tagelyk bleaunen wy krekt itselde yn de databank skriuwe, gewoan mei minder lading.

#2. Databank evolúsje en refactoring

Sa hawwe wy fêstlein op wat wy hawwe elke dei hat syn eigen seksje mei gegevens. Feitlik, CHECK (dt = '2018-10-12'::date) - en der is in partitioning kaai en de betingst foar in rekord te fallen yn in spesifike seksje.

Om't alle rapporten yn ús tsjinst binne boud yn 'e kontekst fan in spesifike datum, binne de yndeksen foar har sûnt "net-ferdielde tiden" alle soarten west (Tsjinner, Datum, Plansjabloan), (Tsjinner, Datum, Planknooppunt), (Datum, Flaterklasse, Server)...

Mar no libje se op elke seksje dyn kopyen elk sa'n yndeks ... En binnen elke seksje datum is in konstante... It docht bliken dat wy no yn elk sa'n yndeks binne gewoan in konstante ynfiere as ien fan 'e fjilden, dat fergruttet sawol syn folume as de syktiid foar it, mar bringt gjin resultaat. Se lieten de hark oan harsels oer, oeps...

Wy skriuwe yn PostgreSQL op sublight: 1 host, 1 dei, 1TB
De rjochting fan optimisaasje is fanselssprekkend - ienfâldich fuortsmite de datum fjild út alle yndeksen op partitionearre tabellen. Sjoen ús folumes is de winst sawat 1TB / wike!

No litte wy opmerke dat dizze terabyte noch op ien of oare manier opnommen wurde moast. Dat is, wy ek de skiif moat no minder lade! Dizze foto toant dúdlik it effekt krigen fan 'e skjinmeitsjen, dêr't wy in wike oan wijd hawwe:

Wy skriuwe yn PostgreSQL op sublight: 1 host, 1 dei, 1TB

#3. "Sprieding" fan de pyk load

Ien fan 'e grutte problemen fan laden systemen is oerstallige syngronisaasje guon operaasjes dy't net nedich it. Soms “omdat se it net yn de gaten ha”, soms “was it makliker sa”, mar ier of let moat je der fan ôf.

Lit ús ynzoome op 'e foarige foto en sjen dat wy in skiif hawwe "pompen" ûnder de lading mei dûbele amplitude tusken neistlizzende samples, dy't dúdlik "statistysk" net moatte barre mei sa'n oantal operaasjes:

Wy skriuwe yn PostgreSQL op sublight: 1 host, 1 dei, 1TB

Dit is frij maklik te berikken. Wy binne al begûn mei tafersjoch hast 1000 tsjinners, elk wurdt ferwurke troch in aparte logyske thread, en elke thread set de sammele ynformaasje werom dy't op in bepaalde frekwinsje nei de databank stjoerd wurde moat, soksawat as dit:

setInterval(sendToDB, interval)

It probleem hjir leit krekt yn it feit dat alle triedden begjinne op likernôch deselde tiid, sadat har ferstjoertiden hast altyd "to the point" gearfalle. Oops #2...

Gelokkich is dit frij maklik te reparearjen, it tafoegjen fan in "willekeurich" oanrin troch tiid:

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

#4. Wy cache wat wy nedich hawwe

It tredde tradisjonele probleem mei hege lading is gjin cache wêr hy is koe wêze.

Wy makken it bygelyks mooglik om te analysearjen yn termen fan planknooppunten (al dizze Seq Scan on users), mar tinke daliks dat se foar it grutste part itselde binne - se fergetten.

Nee, fansels, neat wurdt skreaun oan de databank wer, dit snijt de trekker ôf mei INSERT ... ON CONFLICT DO NOTHING. Mar dizze gegevens berikke noch de databank, en it is net nedich lêzen om te kontrolearjen op konflikt dwaan moatte. Oops #3...

It ferskil yn it oantal records dat nei de databank stjoerd is foardat/nei't caching ynskeakele is, is fanselssprekkend:

Wy skriuwe yn PostgreSQL op sublight: 1 host, 1 dei, 1TB

En dit is de byhearrende daling yn opslachlading:

Wy skriuwe yn PostgreSQL op sublight: 1 host, 1 dei, 1TB

Totaal

"Terabyte-per-day" klinkt gewoan eng. As jo ​​alles goed dogge, dan is dit gewoan 2^40 bytes / 86400 sekonden = ~12.5MB/sdat sels buroblêd IDE screws holden. 🙂

Mar serieus, sels mei in tsienfâldige "skew" fan 'e lading oerdeis, kinne jo de mooglikheden fan moderne SSD's maklik foldwaan.

Wy skriuwe yn PostgreSQL op sublight: 1 host, 1 dei, 1TB

Boarne: www.habr.com

Add a comment