PostgreSQL-ben sublight-on írjuk: 1 host, 1 day, 1TB

Nemrég elmondtam, hogyan, szabványos receptek segítségével az SQL olvasási lekérdezések teljesítményének növelése PostgreSQL adatbázisból. Ma arról fogunk beszélni, hogyan a felvételt hatékonyabban lehet elvégezni az adatbázisban anélkül, hogy bármilyen „csavar” lenne a konfigurációban – egyszerűen az adatfolyamok helyes megszervezésével.

PostgreSQL-ben sublight-on írjuk: 1 host, 1 day, 1TB

#1. Szakaszolás

Egy cikk arról, hogyan és miért érdemes megszervezni alkalmazott particionálás „elméletileg” már volt, itt néhány megközelítés alkalmazásának gyakorlatáról fogunk beszélni figyelő szolgáltatás több száz PostgreSQL szerverhez.

"Elmúlt napok dolgai..."

Kezdetben, mint minden MVP, a mi projektünk is meglehetősen csekély terhelés mellett indult – a monitorozást csak a tíz legkritikusabb szerveren végeztük, az összes tábla viszonylag kompakt volt... De ahogy telt az idő, a figyelt gazdagépek száma egyre több lett. , és még egyszer megpróbáltunk valamit kezdeni az egyikkel 1.5 TB méretű asztalok, rájöttünk, hogy bár lehet így tovább élni, ez nagyon kellemetlen volt.

Az idők majdnem olyanok voltak, mint az epikus idők, a PostgreSQL 9.x különböző verziói relevánsak voltak, így minden particionálást „manuálisan” kellett elvégezni – tábla öröklődése és triggerek útvonalválasztás dinamikussal EXECUTE.

PostgreSQL-ben sublight-on írjuk: 1 host, 1 day, 1TB
A kapott megoldás elég univerzálisnak bizonyult ahhoz, hogy minden táblázatra lefordítható legyen:

  • Üres „fejléc” szülőtáblát deklaráltunk, amely mindent leírt szükséges indexek és triggerek.
  • A kliens szemszögéből történő rögzítés a „gyökér” táblában, belső felhasználással készült útválasztási trigger BEFORE INSERT a rekord „fizikailag” bekerült a kívánt részbe. Ha még nem volt ilyen, kivételt fogtunk és...
  • … használva CREATE TABLE ... (LIKE ... INCLUDING ...) a szülőtábla sablonja alapján jött létre szakaszban a kívánt dátum korlátozásávalhogy az adatok lekérésekor csak abban kerüljön sor az olvasásra.

PG10: első próbálkozás

De az öröklődés útján történő particionálás történelmileg nem volt túl jól alkalmas aktív írási adatfolyammal vagy nagyszámú leszármazott partícióval való munkához. Például felidézheti, hogy a kívánt szakasz kiválasztására szolgáló algoritmus rendelkezett kvadratikus komplexitás, hogy 100+ szakasszal működik, te magad is érted, hogyan...

A PG10-ben ezt a helyzetet nagymértékben optimalizálták a támogatás bevezetésével natív particionálás. Ezért a tárhely migrálása után azonnal megpróbáltuk alkalmazni, de...

Mint a kézikönyv átásása után kiderült, a natívan particionált tábla ebben a verzióban a következő:

  • nem támogatja az indexleírásokat
  • nem támogatja a triggereket rajta
  • nem lehet senki "leszármazottja"
  • ne támogassa INSERT ... ON CONFLICT
  • nem tud automatikusan szakaszt létrehozni

Miután egy gereblyével fájdalmas ütést kaptunk a homlokon, rájöttünk, hogy a pályázat módosítása nélkül lehetetlen, és hat hónappal elhalasztottuk a további kutatást.

PG10: második esély

Tehát egyenként elkezdtük megoldani a felmerülő problémákat:

  1. Mivel kiváltja és ON CONFLICT Azt tapasztaltuk, hogy itt-ott még szükségünk van rájuk, ezért egy köztes szakaszt készítettünk a kidolgozásukra proxy tábla.
  2. Megszabadultam az "útválasztástól" triggerekben – vagyis attól EXECUTE.
  3. Külön kivették sablon táblázat minden indexelhogy a proxy táblában se legyenek jelen.

PostgreSQL-ben sublight-on írjuk: 1 host, 1 day, 1TB
Végül mindezek után natívan particionáltuk a főtáblát. Az új rész létrehozása továbbra is az alkalmazás lelkiismeretére van bízva.

„Fűrészelés” szótárak

Mint minden elemző rendszerben, nálunk is volt "tények" és "kivágások" (szótárak). Nálunk ebben a minőségben jártak el pl. sablon test hasonló lassú lekérdezések vagy maga a lekérdezés szövege.

A „tények” már régóta napra tagolódnak, így nyugodtan töröltük az elavult részeket, és nem zavartak minket (naplók!). De volt egy probléma a szótárral...

Nem azt mondom, hogy sokan voltak, de kb 100 TB „tény” egy 2.5 TB-os szótárat eredményezett. Egy ilyen táblázatból nem lehet kényelmesen törölni semmit, nem lehet megfelelő időben tömöríteni, és az írás is fokozatosan lassabb lett.

Mint egy szótár... benne minden bejegyzést pontosan egyszer kell bemutatni... és ez így van, de!.. Senki sem akadályoz meg bennünket abban, hogy külön szótár minden napra! Igen, ez bizonyos redundanciát hoz, de lehetővé teszi:

  • gyorsabban írni/olvasni kisebb szakaszméret miatt
  • kevesebb memóriát fogyaszt kompaktabb indexekkel dolgozva
  • kevesebb adatot tárol az elavult gyors eltávolításának képessége miatt

Az egész intézkedési komplexum eredményeként CPU terhelés ~30%-kal csökkent, lemezterhelés ~50%-kal:

PostgreSQL-ben sublight-on írjuk: 1 host, 1 day, 1TB
Ugyanakkor továbbra is pontosan ugyanazt írtuk be az adatbázisba, csak kisebb terheléssel.

#2. Adatbázis evolúció és átalakítás

Szóval megállapodtunk abban, amink van minden napnak megvan a maga szakasza adatokkal. Tulajdonképpen, CHECK (dt = '2018-10-12'::date) — és van egy particionáló kulcs és a feltétel, hogy egy rekord egy adott szakaszba kerüljön.

Mivel szolgáltatásunkban minden jelentés egy adott dátum kontextusában készült, a hozzájuk tartozó indexek a „nem particionált idők” óta minden típusú (Szerver, Dátum, tervsablon), (Szerver, Dátum, Terv csomópont), (Dátum, Hibaosztály, Szerver), ...

De most már minden szakaszon élnek a másolatait minden ilyen index... És minden szakaszon belül a dátum állandó... Kiderült, hogy most mindegyik ilyen indexben benne vagyunk egyszerűen írjon be egy állandót mezőként, ami növeli mind a hangerőt, mind a keresési időt, de nem hoz eredményt. Magukra hagyták a gereblyét, hopp...

PostgreSQL-ben sublight-on írjuk: 1 host, 1 day, 1TB
Az optimalizálás iránya nyilvánvaló – egyszerű távolítsa el a dátum mezőt az összes indexből partícionált asztalokon. A mennyiségeinket figyelembe véve a nyereség kb 1TB/hét!

Most jegyezzük meg, hogy ezt a terabájtot valahogy mégis rögzíteni kellett. Vagyis mi is a lemeznek most kevesebbet kell betöltenie! Ezen a képen jól látható a takarítás eredménye, aminek egy hetet szenteltünk:

PostgreSQL-ben sublight-on írjuk: 1 host, 1 day, 1TB

#3. A csúcsterhelés „terítése”.

A betöltött rendszerek egyik nagy baja az redundáns szinkronizálás néhány művelet, amelyhez nincs szükség rá. Néha „mert nem vettek észre”, néha „úgy könnyebb volt”, de előbb-utóbb meg kell szabadulni tőle.

Nagyítsuk fel az előző képet, és nézzük meg, hogy van lemezünk dupla amplitúdójú terhelés alatt „szivattyúznak”. szomszédos minták között, aminek egyértelműen „statisztikailag” nem szabadna megtörténnie ilyen számú műveletnél:

PostgreSQL-ben sublight-on írjuk: 1 host, 1 day, 1TB

Ezt meglehetősen könnyű elérni. Már megkezdtük a megfigyelést közel 1000 szerver, mindegyiket külön logikai szál dolgozza fel, és mindegyik szál alaphelyzetbe állítja az adatbázisba küldendő felhalmozott információkat egy bizonyos gyakorisággal, ilyesmi:

setInterval(sendToDB, interval)

A probléma itt éppen abban rejlik minden szál megközelítőleg ugyanabban az időben kezdődik, így a küldési idők szinte mindig egybeesnek „a lényegre”. Hoppá #2...

Szerencsére ez nagyon könnyen javítható, „véletlenszerű” felfutás hozzáadása idő szerint:

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

#4. Gyorsítótárazzuk, amire szükségünk van

A harmadik hagyományos nagyterhelési probléma az nincs gyorsítótár ahol van tudott lenni.

Például lehetővé tettük a tervcsomópontok szerinti elemzést (mindezek Seq Scan on users), de azonnal arra gondolnak, hogy ezek nagyrészt ugyanazok – elfelejtették.

Nem, persze megint semmi nem íródik az adatbázisba, ez levágja a triggert INSERT ... ON CONFLICT DO NOTHING. De ezek az adatok még mindig eljutnak az adatbázisba, és szükségtelen olvassa el, hogy ellenőrizze a konfliktust meg kell tenni. Hoppá #3...

Nyilvánvaló a különbség az adatbázisba küldött rekordok számában a gyorsítótárazás engedélyezése előtt/után:

PostgreSQL-ben sublight-on írjuk: 1 host, 1 day, 1TB

Ez pedig a tárolási terhelés ezzel járó csökkenése:

PostgreSQL-ben sublight-on írjuk: 1 host, 1 day, 1TB

Összességében

A „Terabyte-per-day” egyszerűen ijesztően hangzik. Ha mindent jól csinálsz, akkor ez az igazság 2^40 bájt / 86400 másodperc = ~12.5 MB/shogy még az asztali IDE csavarok is megtartották. 🙂

De komolyan, még a napközbeni terhelés tízszeres „ferdítésével” is könnyedén megfelelhet a modern SSD-k képességeinek.

PostgreSQL-ben sublight-on írjuk: 1 host, 1 day, 1TB

Forrás: will.com

Hozzászólás