A 2020-es Bruce Momjian előadásának "Az PostgreSQL Lock Manager feloldása" című értekezése.

(Megjegyzés: Minden SQL lekérdezést a diákból ezen a linken kaphat: )
Helló! Csodálatos újra Oroszországban lenni. Elnézést kérek, hogy tavaly nem tudtam eljönni, de idén Ivan-nal nagy terveink vannak. Remélem, sokkal gyakrabban tudok itt lenni. Imádom Oroszországot látogatni. Tyumenbe és Tverbe fogok utazni. Nagyon örülök, hogy ezekben a városokban is járhatok.
Bruce Momjian vagyok. Az EnterpriseDB-nél dolgozom és több mint 23 éve foglalkozom a Postgres-szel. Philadelphia városában élek az Egyesült Államokban. Éves szinten körülbelül 90 napot utazom, és körülbelül 40 konferencián veszek részt. Az én , amely tartalmazza azokat a diaképeket, amelyeket most megmutatok Önöknek. Tehát a konferencia után le tudják tölteni azokat a személyes weboldalamról. Ott körülbelül 30 prezentáció is található. Ezen kívül vannak videók és rengeteg blogbejegyzés, több mint 500. Ez egy meglehetősen tartalmas erőforrás, és ha érdekli ez az anyag, szeretettel invitálom, hogy használja.
Régebben tanár voltam, professzor, mielőtt elkezdtem volna a Postgres-szel dolgozni. Nagyon örülök, hogy most elmondhatom Önöknek azt, amit terveztem. Ez az egyik legérdekesebb prezentációm, amely 110 diát tartalmaz. A legegyszerűbb dolgokkal fogunk kezdeni, és a végére a mondanivaló egyre bonyolultabbá válik, és elég nehéz lesz.

Ez egy meglehetősen kellemetlen beszélgetés. A zárak nem a legnépszerűbb téma. Szeretnénk, ha ez eltűnne valahová. Olyan, mint fogorvoshoz menni.

- A zárak problémát jelentenek sokak számára, akik adatbázisokkal foglalkoznak, és ahol egyszerre több folyamat fut. Szükségük van a zárakra. Tehát ma alapvető ismereteket fogok adni Önöknek a zárakról.
- Tranzakciós azonosítók. Ez egy meglehetősen unalmas része a prezentációnak, de meg kell érteni őket.
- Ezután a zárak típusairól fogunk beszélni. Ez egy elég mechanikus rész.
- Majd példákat mutatunk be a zárakról. Ez pedig elég nehezen érthető lesz.

Beszéljünk a zárakról.

A terminológia elég bonyolult nálunk. Hányan tudják, honnan származik ez a részlet? Két ember. Ez egy játékból származik, amit "Kolosszális kaland a barlangban"-nak hívnak. Ez egy szöveges számítógépes játék volt az 80-as években, ha jól emlékszem. Bementél a barlangba, a labirintusba, és a szöveg változott, de a tartalom nagyjából ugyanaz maradt minden alkalommal. Így emlékszem erre a játékra.

Itt látjuk a blokkolások nevét, amelyek az Oracle-től származnak. Használjuk őket.

Itt olyan kifejezéseket látok, amelyek zavarba ejtenek. Például SHARE UPDATE EXCLUSIVE. Továbbá SHARE RAW EXCLUSIVE. Őszintén szólva, ezek a megnevezések nem igazán érthetőek. Megpróbáljuk részletesebben megvizsgálni őket. Néhány tartalmazza a "share" szót, ami elválást jelent. Néhány tartalmazza az "exclusive" szót - ekskluzív. Van, amelyik mindkét szót tartalmazza. Szeretném elkezdeni azzal, hogyan működnek ezek a blokkolások.

Szintén nagyon fontos a "hozzáférés" - access szó. És a "row" - sor szó. Tehát a hozzáférés elosztása, a sorok elosztása.

Egy másik probléma, amelyet meg kell érteni a Postgres-ben, a MVCC, sajnálatos módon erről a beszédemben nem tudok beszélni. Van egy külön előadásom erről a témáról a weboldalamon. És ha úgy gondolod, hogy ez az előadás bonyolult, akkor a MVCC valószínűleg a legbonyolultabb. És ha érdekel, megnézheted a weboldalon. Nézd meg a videót.

Egy másik szempont, amit meg kell értenünk, az a tranzakció azonosítók. Sok tranzakció nem tud működni egyedi azonosítók nélkül. Itt pedig a tranzakciók jelentése kerül magyarázatba. A Postgres-ben két tranzakciószámozási rendszer van. Tudom, hogy ez nem a legszebb megoldás.

Arra is figyeljetek, hogy a diáink elég bonyolultak lesznek, ezért a pirossal kiemelt részekre kell figyelni.

Nézzük. A pirossal kiemelt a tranzakciós szám. Itt a SELECT pg_back funkció látható. Ez visszaadja az én tranzakciómat és ennek az azonosítóját.
Egy másik dolog, ha tetszik ez a bemutató, és szeretné beállítani az adatbázisában, akkor az alábbi rózsaszínnel kiemelt linkre kattintva letöltheti a SQL-t ehhez a bemutatóhoz. Csak futtassa a PSQL-jében, és az egész bemutató azonnal megjelenik az Ön képernyőjén. Nem fog színeket tartalmazni, de legalább láthatjuk.

Ebben az esetben látjuk a tranzakciós azonosítót. Ez az a szám, amit hozzárendeltünk. Van egy másik típusú tranzakciós azonosító is a Postgresben, amely a virtuális tranzakciós azonosító.
És ezt meg kell értenünk. Nagyon fontos, különben nem tudjuk megérteni a Postgres blokkolást.
A virtuális tranzakciós azonosító olyan tranzakciós azonosító, amely nem tartalmaz állandó értékeket. Például, ha egy SELECT parancsot futtatok, akkor valószínűleg nem fogom megváltoztatni az adatbázist, semmi sem fog blokkolni. Tehát amikor egyszerű SELECT-et futtatunk, nem adunk ennek a tranzakciónak állandó azonosítót. Csak virtuális azonosítót adunk neki.
Ez növeli a Postgres teljesítményét, javítja a tisztítási lehetőségeket, ezért a virtuális tranzakciós azonosító két számjegyből áll. Az első szám a peremén található – ez a háttér azonosító. Jobbra egyszerűen egy számláló látható.

Ezért, ha lekérdezést futtatok, azt mondja, hogy a háttér azonosító – 2.

Ha pedig egy sor ilyen tranzakciót futtatok, láthatjuk, hogy a számláló minden alkalommal növekszik, amikor lekérdezést futtatok. Például, amikor a lekérdezést futtatom 2/10, 2/11, 2/12 stb.

Ne feledje, hogy itt két oszlop van. Bal oldalon látjuk a virtuális tranzakciós azonosítót – 2/12. Jobbra állandó tranzakciós azonosítónk van. Ez a mező üres. Ez a tranzakció nem módosítja az adatbázist. Ezért nem rendeltem hozzá állandó tranzakciós azonosítót.

Amint futtatom az ANALYZE parancsot, ugyanaz a lekérdezés állandó tranzakciós azonosítót ad nekem. Nézze meg, hogyan változott ez. Korábban nem volt ilyen azonosítóm, most pedig van.

Tehát itt van még egy lekérdezés, még egy tranzakció. A virtuális tranzakciós szám – 2/13. És ha megkérem az állandó tranzakciós azonosítót, akkor amikor a lekérdezést futtatom, megkapom azt.

Tehát még egyszer. Van virtuális tranzakciós azonosítónk és állandó tranzakciós azonosítónk. Csak értsük meg ezt a szempontot, hogy megértsük a Postgres viselkedését.

Átérünk a harmadik részre. Itt különböző zárolási típusokat fogunk áttekinteni a Postgresben. Ez nem túl izgalmas. Az utolsó rész sokkal érdekesebb lesz. De előbb meg kell vizsgálnunk az alapvető dolgokat, mert különben nem fogjuk érteni, mi fog következni.
Átmegyünk ezen a részen, megnézzük az egyes zárolási típusokat. Megmutatom, hogyan állítják be őket, hogyan működnek, és néhány lekérdezést is bemutatok, amelyeket használhatsz, hogy lásd, hogyan működik a zárolás a Postgresben.

A lekérdezés létrehozásához és a Postgresben történő események megfigyeléséhez ki kell adnunk egy lekérdezést a rendszer nézetében. Ebben az esetben a pg_lock van kiemelve piros színnel. A pg_lock egy rendszer táblázat, amely megmondja nekünk, hogy aktuálisan milyen zárolások használatosak a Postgresben.
Mindazonáltal nagyon nehéz a pg_lock-ot önállóan megmutatni, mert elég bonyolult. Ezért létrehoztam egy nézetet, amely a pg_locks-ot mutatja. Ez a nézet egyúttal segít nekem abban, hogy jobban megértsem a dolgokat. Tehát kizárja az én zárolásaimat, az én saját munkamenetemet stb. Ez egyszerűen standard SQL, és segít, hogy jobban megmutassam, mi zajlik.

Még egy probléma, hogy ez a nézet nagyon széles, ezért létre kellett hoznom egy második – lockview2.
Ez további oszlopokat mutat meg nekem a táblázatból. És van még egy másik, amely a többi oszlopot mutatja. Ez elég bonyolult, ezért igyekeztem a lehető legegyszerűbben bemutatni.

Tehát létrehoztunk egy táblázatot, amelynek neve Lockdemo. Ezen létrehoztunk egy sort. Ez a példatáblázatunk. Szegmenseket fogunk létrehozni, hogy egyszerűen példákat mutassunk be a zárolásokról.

Tehát egy sor, egy oszlop. Az első zárolási típus neve ACCESS SHARE. Ez a legiskevésbé korlátozó zárolás. Ez azt jelenti, hogy gyakorlatilag nem ütközik a többi zárolással.
Ha kifejezetten meg akarjuk határozni a zárolást, akkor futtatjuk a "lock table" parancsot. Ez egyértelműen zárolni fog, tehát ACCESS SHARE módban futtatjuk a lock table-t. Ha én a PSQL-t a háttérben futtatom, akkor így elindítok egy második munkamenetet az első munkamenetemből. Tehát mit teszek itt? Megyek egy másik munkamenetre, és azt kérem, hogy "mutassa meg a lockview-t ehhez a lekérdezéshez". Itt AccessShareLock van ebben a táblában. Éppen ezt kértem. Azt mondja, hogy a zárolás hozzárendelése megtörtént. Nagyon egyszerű.

Továbbá, ha a második oszlopra nézünk, ott semmi sincs. Üresek.

Ha futtatom a "SELECT" parancsot, akkor ez egy implicit (értelmes) módja az AccessShareLock kérésének. Ezért kiadom a táblámat, és futtatok egy lekérdezést, amely visszaad néhány sor, és az egyik sorban látjuk az AccessShareLock-ot. Így a SELECT AccessShareLock-ot hív elő a táblában. Szinte semmivel sem ütközik, mivel ez egy alacsony szintű zárolás.

Mi van, ha futtatom a SELECT-et, és három különböző táblám van? Korábban csak egy táblát futtattam, most három: pg_class, pg_namespace és pg_attribute.

Most, amikor ránézek a lekérdezésre, 9 AccessShareLock-ot látok három táblában. Miért? Kék színnel kiemelve három tábla: pg_attribute, pg_class, pg_namespace. De azt is látja, hogy minden index, amely ezekkel a táblákkal van definiálva, szintén AccessShareLock-ot kapott.
Ez a zárolás szinte nem ütközik másokkal. Amit csinál, az egyszerűen megakadályozza, hogy visszaállítsuk a táblát, amíg azt kiválasztjuk. Ennek van értelme. Tehát, ha kiválasztunk egy táblát, az abban a pillanatban eltűnik, az helytelen lenne, ezért Az AccessShare egy alacsony szintű zárolás, amely azt mondja nekünk, hogy "ne töröld ezt a táblát, amíg dolgozom".. Lényegében ez az egész, amit csinál.

A ROW SHARE egy kicsit eltérő zárolás.

Nézzük meg a példát. A SELECT ROW SHARE a zárolás módja, amely minden egyes sort külön-külön zárol.. Így senki sem tudja törölni vagy módosítani őket, amíg nézzük.
Tehát, mit is csinál a SHARE LOCK? Látjuk, hogy a tranzakciós azonosító 681 a SELECT-hoz. Ez érdekes. Mi is történt itt? Először látunk egy számot a „Lock” mezőben. Elvesszük a tranzakció azonosítót, és azt mondja, hogy exkluzív módban zárolja. Amit ez ténylegesen jelent, hogy van egy sor, ami technikailag valahol a táblában zárolva van. De nem mondja meg, hogy hol pontosan. Később részletesebben is megnézzük ezt.

Itt elmondjuk, hogy a zárolást mi használjuk.

Tehát, az exkluzív zárolás egyértelműen (explicit) azt mondja, hogy exkluzív. És ha törölsz egy sort ebben a táblában, akkor ez valóban meg fog történni, ahogy azt láthatod.

A SHARE EXCLUSIVE egy hosszabb zárolás.

Ez (ANALYZE) az elemző parancs, amelyet használni fogunk.

SHARE LOCK – kifejezetten zárolhatod megosztási módban.

Egyedi indexet is létrehozhatsz. Ott láthatod a SHARE LOCK-ot, ami annak a része. És ez zárolja a táblát, és beállít rá egy SHARE LOCK-ot.
Alapértelmezés szerint a SHARE LOCK a táblán azt jelenti, hogy mások olvashatják a táblát, de senki sem módosíthatja. És pontosan ez történik, amikor egyedi indexet hozol létre.
Ha egyedi párhuzamos indexet hozok létre, akkor más típusú zárolásom lesz, mert, ahogy emlékszel, a párhuzamos indexek használata csökkenti a zárolási igényt. És ha normál zárolást használok, normál indexet, akkor ezzel megakadályozom a táblázat indexébe való írást annak létrehozása során. Ha párhuzamos indexet használok, akkor más típusú zárolást kell használnom.

A SHARE ROW EXCLUSIVE – ezt is kifejezetten (explicit) be lehet állítani.

Vagy létrehozhatunk egy szabályt, azaz vegyünk egy bizonyos esetet, amelyben használva lesz.

Az EXCLUSIVE zárolás azt jelenti, hogy más senki nem tudja megváltoztatni a táblát.

Itt látunk különböző típusú zárolásokat.

Az ACCESS EXCLUSIVE például egy zárolási parancs. Például, ha végzel CLUSTER table, akkor ez azt fogja jelenteni, hogy senki nem tud oda írni. És ez nemcsak magát a táblázatot zárolja, hanem az indexeket is.

Ez az ACCESS EXCLUSIVE zárolás második oldala, ahol konkrétan látjuk, hogy mit zárol a táblázatban. Ez a táblázat különböző sorait zárolja, ami elég érdekes.
Ez az összes alapvető információ, amit meg akartam osztani. Beszéltünk a blokkokról, a tranzakciós azonosítókról, a virtuális tranzakciós azonosítókról és a tartós tranzakciós azonosítókról.

Most pedig néhány példa blokkokra következik. Ez a legérdekesebb rész. Nagyon érdekes eseteket vizsgálunk meg. Az én feladatom ebben a prezentációban, hogy a lehető legjobb képet adjak arról, hogy a Postgres valójában mit csinál, amikor blokkolni próbál bizonyos dolgokat. Úgy tűnik, hogy nagyon jól tudja kezelni az egyes részek blokkolását.
Nézzük meg bizonyos példákat.

Kezdjük a táblázatokkal és egy sorral a táblázatban. Amikor beillesztek valamit, megjelenik az ExclusiveLock, a tranzakciós azonosító és az ExclusiveLock a táblázaton.

Mi történik, ha még két sort illesztek be? Most a táblázatunkban három sor van. Beillesztettem egy sort, és ezt kaptam meg eredményként. Ha még két sort illesztek be, mi a furcsa itt? Furcsaság van, mert három sort adtam hozzá ehhez a táblázathoz, de a blokkolt táblázatban még mindig csak két sor van. Ez lényegében a Postgres alapvető viselkedése.
Sokan úgy gondolják, hogy ha a adatbázisban 100 sort blokkolnak, akkor 100 blokkolási bejegyzést kell létrehozni. Ha egyszerre 1 000 sort blokkolok, akkor 1 000 ilyen lekérdezésre lesz szükségem. És ha milliót vagy milliárdot kell blokkolnom. De ha így csináljuk, az nem fog jól működni. Ha olyan rendszert használsz, amely blokkolási bejegyzéseket készít minden egyes sorra, látni fogod, hogy ez bonyolult. Mert meg kell határozni egy blokkolási táblát, amely túl tud tölteni, de a Postgres nem így működik.
Ennél a dián rendkívül fontos, hogy világosan bemutatásra kerül, hogy van még egy rendszer, amely az MVCC-n belül működik, és amely egyes sorokat blokkol. Tehát amikor milliárd sort blokkolsz, a Postgres nem készít milliárd külön blokkparancsot. Ez nagyon jótékonyan hat a teljesítményre.

Mi a helyzet a frissítéssel? Jelenleg frissítek egy sort, és észreveheted, hogy rögtön két különböző műveletet végrehajtott. Egyszerre zárolta a táblát, de zárolta az indexet is. Zárolnia kellett az indexet, mert vannak egyedi korlátozások ezen a táblán. Azt szeretnénk biztosítani, hogy senki se módosítsa, ezért zároljuk.

Mi történik, ha két sort szeretnék frissíteni? És látjuk, hogy ugyanúgy viselkedik. Kétszer annyi frissítést végzünk, de ugyanannyi sor zárolást hajtunk végre.
Ha érdekel, hogyan csinálja ezt a Postgres, hallgatnod kell a MVCC-ről szóló előadásaimat, hogy megtudd, hogyan jelöli meg a Postgres belsőleg azokat a sorokat, amelyeket megváltoztat. A Postgres-nek van egy módszere, amellyel ezt megteszi, de nem a táblák zárolásának szintjén, hanem egy alacsonyabb és hatékonyabb szinten.

Mi van, ha valamit el akarok távolítani? Ha például eltávolítok egy sort, és még mindig van két zárolásom, és még ha mindet el akarom távolítani, akkor is ott maradnak.

Például, ha 1000 sort szeretnék hozzáadni, majd eltávolítani vagy újabb 1000 sort hozzáadni, akkor azok az egyedi sorok, amelyeket hozzáadok vagy módosítok, nem itt lesznek rögzítve. Azok egy alacsonyabb szinten rögzítődnek magában a sorban. A MVCC-ről szóló előadásomban részletesen beszéltem erről. De nagyon fontos, amikor elemzed a zárolásokat, hogy győződj meg arról, hogy van táblaszintű zárolásod, és hogy itt nem látod, hogyan rögzítik az egyes sorokat.

Mi a helyzet az explicit zárolással?

Ha rákattintok a „frissítés” gombra, akkor két zárolt sorom van. És ha mindkettőt kijelölöm, és megnyomom a „frissítés mindenhol” gombot, akkor még mindig két zárolás marad.

Nem hozunk létre külön bejegyzéseket minden egyes sorra. Mert akkor csökkent a teljesítmény, és lehet, hogy túl sok ilyet hozunk létre. És kellemetlen helyzetbe kerülhetünk.

Ugyanez igaz, ha megosztott zárolást végzünk, akkor ezt 30 alkalommal is megtehetjük.

Visszaállítjuk a táblánkat, mindent eltávolítunk, majd újra beszúrunk egy sort.

Egy másik viselkedésmód, amit a Postgresben láthat, az a jól ismert és kívánatos viselkedés – hogy egyszerre végezhet UPDATE vagy SELECT műveletet. A SELECT nem blokkolja az UPDATE-et, és fordítva is így van. Ez azt jelenti, hogy a olvasó nem blokkolja az írókat, és az írók sem blokkolják az olvasókat.
Megmutatom ennek egy példáját. Most választást fogok végezni. Majd INSERT-et végzünk. Akkor láthatja – 694. Meg fogja látni a tranzakció azonosítóját, amelyik ezt a beszúrást végezte. Így működik.

Ha most megnézem a backend ID-met, akkor ez most 695 lett.

Látom, hogy a 695 megjelenik a táblázatomban.

Ha itt így végzek egy frissítést, akkor egy másik esetet kapok. Ebben az esetben a 695 - kizárólagos zárolás, és az UPDATE-nek is hasonló a viselkedése, de közöttük nem merül fel konfliktus, ami meglehetősen szokatlan.
Megjegyezheti, hogy fent – ez egy ShareLock, alul pedig – ez egy ExclusiveLock. Mindkét tranzakció megtörtént.
Meg kell hallgatnia az MVCC előadásomat, hogy megértse, hogyan történik mindez. De ez egy illusztráció arra, hogy ezeket egyszerre meg lehet csinálni, azaz egy időben végezhet SELECT-et és UPDATE-et.

Töröljük le és végezzünk el ismét egy műveletet.

Ha megpróbál egyszerre két UPDATE-et futtatni ugyanazon a soron, akkor az zárolódik. Ne feledje, hogy azt mondtam, hogy az olvasó nem blokkolja az írót, az író pedig az olvasót, de egy író blokkolja a másik írót. Tehát nem végezhetünk el két frissítést egy időben ugyanazon a soron. Várni kell, amíg az egyik befejezi.

Ennek illusztrálására megnézem a Lockdemo táblázatot. Megnézzük az egyik sort. A tranzakciót 698.
Ezt frissítettük 2-re. 699 – ez az első frissítés. És ez sikeresen végbement, vagy éppen várakozik a tranzakcióra, és arra vár, hogy jóváhagyjuk vagy visszavonjuk.

De nézd meg a másikat – 2/51 – ez a mi első tranzakciónk, az első ülésünk. 3/112 – ez a második kérés, amely felmerült, és amely megváltoztatta ezt az értéket 3-ra. Ha észreveszed, a felső önmagát blokkolta, ami 699. De a 3/112 nem biztosított blokkolást. A Lock_mode oszlopban az áll, hogy várakozik. A 699-re vár. Ha megnézed, hol van 699, az fent van. És mit tett az első ülés? Létrehozott egy kizárólagos blokkolást a saját tranzakciós ID-jén. Így működik a Postgres. Az blokkolja a saját tranzakciós ID-jét. És ha várni akarsz, amíg valaki megerősíti vagy visszavonja, akkor várni kell, amíg van egy várakozó tranzakció. Ezért láthatunk egy furcsa sort.
Nézzük meg még egyszer. Balra látjuk a feldolgozó ID-nket. A második oszlopban látjuk a tranzakció virtuális ID-ját, a harmadikban pedig a lock_type-ot. Mit jelent ez? Lényegében azt mondja, hogy blokkolja a tranzakciós ID-t. De vedd észre, hogy az alsó sorokban mindnél a relation írás szerepel. Ezért kétféle blokkolás van a táblázatban. Van relation blokkolás. Ezen kívül van transactionid blokkolás is, ahol saját magunkat blokkoljuk, éppen az történik az első sorban, vagy legalul, ahol a transactionid van, ahol arra várunk, hogy a 699 befejezze a műveletét.
Nézem, mi történik itt. Itt egyszerre két dolog történik. Te a tranzakciós ID blokkra nézel az első sorban, ami magát blokkolja. És magát blokkolja, hogy az embereket várakoztassa.
Ha megnézed a 6. sort, az ugyanaz a bejegyzés, mint az első. Ezért a 699-es tranzakció blokkolódik. A 700 is önmagát blokkolja. Aztán az alsó sorban látni fogod, hogy várakozunk, amíg a 699 befejezi a műveletét.

A lock_type, tuple-ban láthatod a számokat.

Láthatod, hogy ez 0/10. Ez az oldal száma, és az adott sor offset-je is.

Látod, hogy 0/11-re változik, amikor frissítünk.

De valójában ez 0/10, mert a művelet várakozása történik. Lehetőség van arra, hogy megnézze, hogy ez az a sor, amelyre várok, hogy megerősítsem.

Miután megerősítettük és rákattintottunk a commit gombra, és miután a frissítés befejeződött, ez az, amit újra kapunk. A 700-as tranzakció az egyetlen zárolás, már senkire sem vár, mert elkötelezték. Csak azt várja, hogy a tranzakció befejeződjön. Amint a 699 befejeződik, már semmire sem várunk. Most a 700-as tranzakció azt mondja, hogy minden rendben van, az összes szükséges zárolás benne van az összes engedélyezett táblában.

És hogy még bonyolultabbá tegyük a dolgot, létrehozunk egy új nézetet, amely ezúttal egy hierarchiát nyújt nekünk. Nem várom el, hogy megértsd ezt a lekérdezést. De ez világosabb képet ad arról, mi történik.

Ez egy rekurzív nézet, amelynek van egy másik szekciója is. Ezután visszatér az egészhez. Használjuk ezt.

Mi lenne, ha három egyidejű frissítést végeznénk, és azt mondanánk, hogy a sor most három. Kicseréljük a 3-at 4-re.

És itt látjuk a 4-est. És a tranzakciós ID 702.

Aztán kicserélem a 4-et 5-re, az 5-öt 6-ra, a 6-ot 7-re. Sorba állítok embereket, akik várakozni fognak arra, hogy ez az egy tranzakció befejeződjön.

És minden világossá válik. Mi az első sor? Ez a 702. Ez a tranzakciós ID, amely eredetileg ezt az értéket adta. És mit írtam a Granted oszlopba? Van feljegyzésem f. Ezek az én frissítéseim, amelyek (5, 6, 7) nem kerülhetnek jóváhagyásra, mert arra várunk, hogy a tranzakciós ID 702 befejeződjön. Ott van a tranzakciós ID zárolása. És így 5 tranzakciós zárolás ID keletkezik.
Ha megnézel a 704-re, a 705-re, akkor ott még semmi sincs leírva, mert még nem tudják, mi történik. Csak annyit írnak, hogy fogalmuk sincs, mi történik. És csak aludni fognak, mert várják, hogy valaki befejezze, és felébresszék őket, amikor lehetőség lesz a sor módosítására.

Így néz ki. Világos, hogy mindannyian a 12-es sorra várnak.

Ez az, amit itt láttunk. Itt van 0/12.

Tehát, amint az első tranzakció jóváhagyásra kerül, itt láthatod, hogyan működik a hierarchia. És most minden világossá válik. Mindannyian tiszták lesznek. Valójában még mindig várakoznak.

Ez történik. 702 elköteleződik. Most 703 blokkolja ezt a sort, és aztán 704 vár arra, hogy 703 elköteleződjön. 705 is erre vár. Amikor ez mind befejeződik, akkor magukat kitakarítják. Szeretném megjegyezni, hogy mindannyian sorba állnak. Ez nagyon hasonlít a forgalmi dugóra, amikor mindenki az első autóra vár. Az első autó megállt, és mindenki hosszú sorba áll. Aztán elindul, majd a következő autó előreléphet, és megkaphatja a blokkját, és így tovább.

És ha ez nem tűnt elég bonyolultnak, most fogunk beszélni a holtpontokról. Nem tudom, ki tapasztalt már ilyet. Ez egy elég gyakori probléma az adatbázis rendszerekben. A holtpontok az a helyzet, amikor az egyik munkamenet vár arra, hogy valami végrehajtódjon egy másik munkamenet által. Eközben a másik munkamenet vár arra, hogy az első végrehajtson valamit.
Például, ha Iván azt mondja: "Adj nekem valamit", én meg azt mondom: "Nem, csak akkor adom, ha te adsz nekem valami mást." Ő meg azt válaszolja: "Nem adom neked ezt, hacsak te nem adsz nekem valamit." És így holtpontra kerülünk. Biztos vagyok benne, hogy Iván nem így fog tenni, de érted a lényeget: két ember szeretne valamit kapni, de nem hajlandóak adni, amíg a másik nem adja meg, amit akarnak. Itt nincs megoldás.
Alapvetően az adatbázisodnak ezt fel kell ismernie. Aztán meg kell szüntetnie vagy lezárnia kell az egyik munkamenetet, mert különben örökre ott maradnak. Ezt látjuk az adatbázisokban, ezt látjuk az operációs rendszerekben. Mindenhol, ahol párhuzamos folyamatok vannak, ez előfordulhat.

Most képezünk két holtpontot. Beállítjuk 50 és 80-ra. Az első sorban frissítem 50-ről 50-re. A tranzakciós számom 710 lesz.

Aztán 80-at 81-re változtatom, és 50-et 51-re.

Így fog kinézni. Így 710 blokkolja a sort, míg 711 megerősítésre vár. Ezt láttuk, amikor frissítettük. 710 birtokolja a sorunkat. A 711 vár arra, hogy 710 befejezze a tranzakciót.

És ott még az is fel van tüntetve, hogy melyik sorban történik a holtpont. Itt kezd érdekes lenni a dolog.

Most frissítjük 80-at 80-ra.

És itt kezdődik a deadlock. A 710-es várakozik a 711-es válaszára, és a 711-es várakozik a 710-esre. És ennek nem lesz jó vége. Nincs kiút ebből. Egymás válaszára fognak várni.

És ez mind késlekedni fog. Ezt nem akarjuk.

A Postgresban van mód a figyelésre, amikor ez megtörténik. És amikor ez megtörténik, kap egy ilyen hibát. És ebből világos, hogy egy meghatározott folyamat SHARE LOCK-ot vár egy másik folyamattól, amelyet a 711-es blokkol. Az a folyamat várta, hogy SHARE LOCK-ot kapjon egy adott tranzakciós ID-ra, és blokkolja valamilyen másik folyamat. Itt tehát deadlock helyzetről van szó.

De létezik háromoldalú deadlock? Ez lehetséges? Igen.

Beírjuk ezeket a számokat a táblázatba. Cseréljük a 40-et 40-re, blokkolunk.

Cseréljük a 60-at 61-re, a 80-at pedig 81-re.

Aztán a 80-at módosítjuk, és – bum!

Most a 714-es a 715-ösre vár. A 716-os a 715-ösre vár. Ezzel már nem lehet mit tenni.

Itt már nem csak két ember van, hanem három. Én szeretnék valamit tőled, ő szeretne valamit a harmadik embertől, a harmadik ember pedig szeretne valamit tőlem. Így hármas várakozásba kerülünk, mivel mindannyian arra várunk, hogy a másik befejezze, amit tennie kell.

A Postgres tudja, hogy ez melyik sorban történik. Ezért egy olyan üzenetet ad ki, amely megmutatja, hogy problémája van, ahol a három bejegyzés egymást blokkolja. Itt nincsenek korlátozások. Ez előfordulhat olyan esetben, amikor 20 bejegyzés blokkolja egymást.

A következő probléma a serializálható.

Ha speciális serializálható zár.

Visszatérünk a 719-hez. Ennek teljesen normális a kimenete.

Gombra kattintva tranzakciót végezhet serializálható módba.

És most már tudja, hogy van egy másik zár típusa SA – ez a serializálható.


Ezért van egy új zár típusunk, amely SARieadLock néven ismert, ez egy sorozatos zár, és lehetővé teszi a sorozatok bevitelét.

Ezenkívül egyedi indexeket is be lehet illeszteni.

Ebben a táblázatban egyedi indexeink vannak.

Ha ide beírok egy 2-t, akkor van egy 2-m. De a legfelül még egy 2-t illesztek be. Látja, hogy a 721-nek kizárólagos zára van. De most a 722 vár arra, hogy a 721 befejezze a műveletét, mert nem tudja beilleszteni a 2-t, amíg nem tudja, mi történik a 721-gyel.

Ha altranzakciót hajtunk végre.

Itt van a 723.

Ha megőrizzük a pontot, majd frissítjük, akkor új tranzakciós ID keletkezik. Ez még egy viselkedési jellemző, amiről tudnod kell. Ha visszaadjuk ezt, akkor a tranzakciós ID eltűnik. A 724 eltűnik. De most megjelenik a 725.
És mit próbálok itt csinálni? Példákat szeretnék bemutatni a szokatlan zárolásokra, amelyeket találhatsz: legyen szó serializable zárolásokról vagy SAVEPOINT-ról – ezek különböző típusú zárolások, amelyek megjelennek a zárolási táblában.

Ez explicit (kézi) zárolások létrehozása, ahol pg_advisory_lock.

Látod, hogy a zárolás típusa itt tanácsadónak van feltüntetve. Itt pedig pirossal van írva „advisory”. Ezt egyszerre lehet zárolni a pg_advisory_unlock segítségével.

És a befejezésül szeretnék még egy érdekes dolgot bemutatni. Létrehozok egy másik típust. De összekapcsolom a pg_locks táblát a pg_stat_activity táblával. Miért szeretném ezt csinálni? Mert ez lehetővé teszi számomra, hogy megnézhessem és láthassam az összes aktív munkamenetet, és megfigyelhessem, hogy milyen zárolásokra várnak. Ez eléggé érdekes, amikor összegyűjtjük a zárolási táblát és a lekérdezések tábláját.

Itt létrehozzuk a pg_stat_view-t.

Frissítjük az egy sort egyel. Itt látjuk a 724-et. Majd frissítjük a sorunkat háromra. Mit látsz most itt? Ezek a lekérdezések, tehát látod a bal oszlopban felsorolt összes lekérdezést. A jobb oldalon pedig láthatod a zárolásokat és azt, hogy mit hoznak létre. Esetleg érthetőbbé válik számodra, hogy ne kelljen minden alkalommal visszatérned minden munkamenethez, hogy megnézd, szükség van-e csatlakozni hozzájuk vagy sem. Ezt helyettünk elvégzik.
Egy másik nagyon hasznos funkció az pg_blocking_pidsValószínűleg soha nem hallott róla. Mit csinál? Lehetővé teszi számunkra, hogy megmondjuk, hogy az 11740-es munkamenethez milyen ID-procesei vannak elvárva. És láthatja, hogy az 11740 724-et vár. A 724 a legfelsőbb helyen van. Az 11306 pedig az ön ID-processzora. Alapvetően ez a funkció bejárja a zárolási tábláját. Tudom, hogy ez egy kicsit bonyolult, de meg tudja ezt érteni. Alapvetően ez a funkció végigmegy ezen a zárolási táblán, és próbálja megtalálni, hol van ez a process ID, figyelembe véve a zárolásokat, amelyeket vár. És megpróbálja kiszámolni, hogy melyik process ID-je van annak a folyamatnak, amelyik vár a zárolásokra. Ezért elindíthatja ezt a funkciót. pg_blocking_pids.
Ez nagyon hasznos lehet. Ezt csak a 9.6-os verzió óta adtuk hozzá, így ez a funkció mindössze 5 éves, de nagyon és nagyon hasznos. Ugyanez vonatkozik a második kérésre is. Az pontosan azt mutatja, amit látni akarunk.

Ez az, amiről beszélni akartam önnel. Ahogy vártam, az összes időnket felhasználtuk, mert annyi diánk volt. A diákat le lehet tölteni. Szeretném megköszönni, hogy itt volt. Biztos vagyok benne, hogy élvezni fogja a konferencia hátralévő részét, nagyon köszönöm!
Kérdések:
Például, ha próbálom frissíteni a sorokat, míg a második munkamenet próbálja törölni az egész táblát. Amennyire tudom, ott kell lennie valaminek, mint egy szándékos zárolás. Van ilyen a Postgresben?

Visszatérve az elejére. Talán emlékszik, hogy amikor bármit csinál, például amikor SELECT-t használ, kiadunk AccessShareLock-ot. Ez megakadályozza az egész tábla törlését. Szóval, ha például frissíteni próbál egy sort a táblában vagy törölni egy sort, akkor valaki nem tudja egyszerre törölni az egész táblát ezzel, mert rajta van ez az AccessShareLock az egész táblán és azon a soron. És amint befejezte, törölhetik. De amíg közvetlenül valamit ott változtat, nem tudják megtenni.
Nézzük meg újra. Térjünk rá a törlés példájára. És látja, hogy egy exkluzív zárolás van az egész táblán.
Ennek így exkluzív zárolásnak kell kinéznie, igaz?
Igen, ez így néz ki. Értem, miről beszél. Azt mondja, hogy ha SELECT-et végzek, akkor ShareExclusive-re váltok, majd ha ezt Row Exclusive állapotba helyezem, akkor ez problémát jelent? De meglepő módon ez nem okoz problémát. Olyan, mintha megnőne a zárolás mértéke, de lényegében van egy zárolásom, amely megakadályozza a törlést. És most, amikor ezt a zárolást erősebbé teszem, még mindig megakadályozza a törlést. Tehát nem arról van szó, hogy felfelé haladok. Vagyis megakadályozta ezt, amikor még alacsonyabb szinten volt, ezért amikor felemelem a szintet, akkor is megakadályozza a táblázat törlését.
Értem, miről beszél. Itt nincs olyan eset, amikor a zárolás mértékének növelése történik, ahol megpróbálja feladni egy zárolást, hogy egy erősebbet vezessen be. Itt egyszerűen mindenhol fokozza ezt a megakadályozást, ezért nem okoz konfliktust. De ez egy jó kérdés. Köszönöm, hogy feltette!
Mit kell tennünk ahhoz, hogy elkerüljük a deadlock helyzetet, amikor sok session és sok felhasználó van?
A Postgres automatikusan észleli a deadlock helyzeteket. És automatikusan eltávolít egy session-t. Az egyetlen módja annak, hogy elkerüljük a holt zárolásokat, ha az embereket ugyanabban a sorrendben zárjuk le. Tehát amikor megnézi az alkalmazását, gyakran a deadlock-ok oka… Tegyük fel, hogy két különböző dolgot szeretnék zárolni. Az egyik alkalmazás zárolja az 1-es táblázatot, a másik alkalmazás pedig a 2-est, majd az 1-est. A legegyszerűbb módja a deadlock-ok elkerülésének az, hogy megnézze az alkalmazását, és megpróbálja biztosítani, hogy a zárolás ugyanabban a sorrendben történjen minden alkalmazásban. Ez általában eltávolítja a problémák 80%-át, mert sokféle ember írja ezeket az alkalmazásokat. Ha ugyanabban a sorrendben zárolja őket, akkor nem fog találkozni deadlock helyzettel.
Nagyon köszönöm az előadását! Beszélt a vacuum full-ról, és ha jól értem, akkor a vacuum full deformálja a rekordok sorrendjét egyetlen tárolón belül, így a jelenlegi rekordokat változatlanul tartja. Miért igényel a vacuum full kizárólagos zárolást, és miért ütközik a írási műveletekkel?
Ez egy jó kérdés. Az oka az, hogy a vacuum full elér egy táblát. Lényegében egy új verziót hozunk létre a táblából. A tábla új lesz. Tehát ez tulajdonképpen egy teljesen új verziója a táblának. A probléma az, hogy amikor ezt csináljuk, nem akarjuk, hogy az emberek olvassák, mert szeretnénk, ha látják az új táblát. Ezért kapcsolódik ez az előző kérdéshez. Ha egyszerre tudnánk olvasni, akkor nem tudnánk azt áthelyezni és az embereket az új táblához irányítani. Várnunk kellene, hogy mindenki befejezze a régi tábla olvasását, és ezért lényegében ez egy exkluzív zárási helyzet.
Csak annyit mondunk, hogy az elején zároljuk, mert tudjuk, hogy a végén exkluzív zárásra lesz szükségünk, hogy mindenkit áttolhassunk az új példányra. Ezért potenciálisan engedélyezhetjük. Így csináljuk ezt párhuzamos indexelés mellett. De ez sokkal nehezebb megvalósítani. Ez nagyon szoros összefüggésben áll az előző kérdésével az exkluzív zárral kapcsolatban.
Lehetséges locking timeoutot hozzáadni a Postgreshez? Az Oracle-ben például írhatom, hogy „válaszd az update-t”, és 50 másodpercig várok a frissítésre. Ez jól működött az alkalmazás számára. De a Postgresben vagy azonnal meg kell tennem, és egyáltalán nem várhatok, vagy várnom kell egy bizonyos ideig.
Igen, választhat késleltetést a zárolásaihoz, a locks-hoz. Kiadással olyan parancsot is adhat, ami …, ha nem tud azonnal zárolni. Tehát vagy lock timeout, vagy valami más, ami lehetővé teszi ezt. Ez nem a szintaktikai szinten történik. Ez egy szerverváltozóként működik. Néha ezt nem lehet használni.
Meg tudod nyitni a 75-ik diát?
Igen.

A kérdésem a következő. Miért vár mindkét frissítési folyamat 703-ra?
Ez egy nagyszerű kérdés. Igazából nem értem, hogy miért csinálja ezt a Postgres. De amikor a 703-as létrejött, akkor a 702-es volt a várakozásban. És amikor a 704-es és 705-ös megjelenik, úgy tűnik, hogy nem tudják, mit várnak, mert még semmi sincs ott. A Postgres így csinálja: amikor nem tudja megszerezni a zárolást, azt mondja, hogy 'Miért is dolgoznánk veled?', mert már úgyis valakire vársz. Szóval hagyjuk, hogy csak lógjon a levegőben, mert egyáltalán nem frissíti ezt. De mi történt itt? Amint a 702-es befejezte a folyamatot és a 703-as megkapta a zárolását, a rendszer visszatért. Azt mondta, hogy most már van két ember, aki várakozik. Aztán frissítsük őket együtt. És jelezzük, hogy mindketten várakoznak.
Nem tudom, miért csinálja ezt a Postgres. De van egy probléma, amit f...-nak hívnak. Szerintem ez nem egy orosz kifejezés. Ez az, amikor mindenki ugyanarra a zárolásra vár, még ha 20 példány is várakozik a zárolásra. És hirtelen mind egyszerre felébrednek. És mindenki próbál reagálni. De a rendszer úgy csinálja, hogy mindenki a 703-ra vár. Mivel mindenki vár, az összeset azonnal sorba állítjuk. És ha megérkezik bármilyen új kérés, amelyet ezután adtak be, például a 707-es, akkor ott újra üresség lesz.
Szerintem ezt azért csinálják, hogy el lehessen mondani, hogy ezen a ponton a 702-es a 703-asra vár, míg azoknak, akik ezután jönnek, nem lesz bejegyzésük ezen a mezőn. De amint az első várakozó távozik, és mindenki, aki abban az időben várt a frissítés előtt, megkapja ugyanazt a jelet. Ezért úgy gondolom, hogy ezt azért csinálták, hogy sorban tudjuk őket kezelni, hogy helyesen legyenek rendezve.
Mindig is egy elég furcsa jelenségként tekintettem rá. Mert itt például egyáltalán nem soroljuk fel őket. De úgy érzem, hogy mindig, amikor új zárolást adunk, megnézzük az összes várakozó folyamatot. Akkor mindannyiukat sorba állítjuk. És aztán bármely új, aki jön, csak akkor kerül a sorba, amikor a következő ember befejezte a feldolgozást. Nagyon jó kérdés. Köszönöm szépen a kérdést!
Szerintem logikusabb lenne, ha a 705-ös a 704-esre várna.
A probléma itt a következő. Technikailag felébreszthetjük ezt az egyet vagy azt a másikat. Éppen ezért fel fogjuk ébreszteni ezt vagy azt. De mi történik a rendszer működésében? Látja, hogy 703 a legfelső részen blokkolta a saját tranzakciós ID-ját. Így működik a Postgres. És 703 a saját tranzakciós ID-jától blokkolva van, ezért ha valaki várni akar, akkor várni fog 703-ra. És lényegében 703 befejezi. Csak miután befejeződött, valamelyik folyamat ébred fel. És nem tudjuk, pontosan melyik folyamat lesz az. Ezután fokozatosan mindent feldolgozunk. De nem világos, hogy melyik folyamat ébred fel először, mert bármelyik ezek közül lehet. Lényegében volt egy ütemezőnk, amely azt mondta, hogy most ébresszük fel azon folyamatok bármelyikét. Csak véletlenszerűen választunk egyet. Ezért mindkettőt meg kell jelölni, mert bármelyiket fel tudjuk ébreszteni.
A probléma az, hogy nekünk CP-végtelenítésünk van. És ezért valószínűleg felébreszthetünk egy későbbit. Ha például a későbbit ébresztjük fel, akkor arra fogunk várni, aki éppen blokkolást kapott, és ezért nem tudjuk meghatározni, ki lesz az, aki először ébred fel. Csak egy ilyen helyzetet teremtünk, és a rendszer véletlenszerű rendben fogja felébreszteni őket.
Van . Nézze, ezek is érdekesek és hasznosak. A téma természetesen borzasztóan bonyolult. Nagyon köszönöm, Bruce!
Forrás: habr.com
