Transcriptie van de lezing van Bruce Momjian uit 2020 "Unlocking the Postgres Lock Manager".
(Opmerking: alle SQL-query's van de dia's kunnen via deze link worden verkregen:
Hallo! Het is geweldig om weer hier in Rusland te zijn. Het spijt me dat ik vorig jaar niet kon komen, maar dit jaar hebben Ivan en ik grootse plannen. Ik hoop hier nog veel vaker te zijn. Ik kom graag naar Rusland. Ik zal Tyumen, Tver, bezoeken. Ik ben erg blij dat ik deze steden kan bezoeken.
Mijn naam is Bruce Momjian. Ik werk bij EnterpriseDB en werk al meer dan 23 jaar met Postgres. Ik woon in Philadelphia, VS. Ik reis ongeveer 90 dagen per jaar. En ik woon ongeveer 40 conferenties bij. Mijn
Voordat ik met Postgres begon te werken, was ik leraar en professor. En ik ben heel blij dat ik u nu kan vertellen wat ik u ga vertellen. Dit is een van mijn meest interessante presentaties. En deze presentatie bevat 110 dia's. We zullen over eenvoudige dingen beginnen te praten, en tegen het einde zal het rapport steeds complexer worden, en behoorlijk complex.
Dit is een nogal onaangenaam gesprek. Blokkeren is niet het meest populaire onderwerp. Wij willen dat dit ergens verdwijnt. Het is alsof je naar de tandarts gaat.
- Vergrendelen is een probleem voor veel mensen die in databases werken en meerdere processen tegelijkertijd laten draaien. Ze hebben blokkering nodig. Dat wil zeggen, vandaag zal ik je basiskennis geven over blokkeren.
- Transactie-ID's. Dit is een nogal saai onderdeel van de presentatie, maar ze moeten wel begrepen worden.
- Vervolgens zullen we het hebben over soorten blokkering. Dit is een redelijk mechanisch onderdeel.
- En hieronder zullen we enkele voorbeelden van blokkeren geven. En het zal behoorlijk moeilijk te begrijpen zijn.
Laten we het hebben over blokkeren.
Onze terminologie is behoorlijk complex. Hoeveel van jullie weten waar deze passage vandaan komt? Twee personen. Dit komt uit een spel genaamd Colossal Cave Adventure. Het was een op tekst gebaseerd computerspel uit de jaren 80, denk ik. Daar moest je een grot in, een labyrint in, en de tekst veranderde, maar de inhoud was elke keer ongeveer hetzelfde. Zo herinner ik mij dit spel.
En hier zien we de naam van de sloten die vanuit Oracle naar ons toe zijn gekomen. Wij gebruiken ze.
Hier zien we termen die mij verwarren. Bijvoorbeeld DEEL UPDATE ECXLUSIVE. Volgende DEEL RAW ECXLUSIVE. Eerlijk gezegd zijn deze namen niet erg duidelijk. We zullen proberen ze in meer detail te bekijken. Sommige bevatten het woord ‘delen’, wat scheiden betekent. Sommige bevatten het woord ‘exclusief’. Sommige bevatten beide woorden. Ik wil graag beginnen met hoe deze sloten werken.
En het woord ‘toegang’ is ook erg belangrijk. En de woorden "rij" zijn een string. Dat wil zeggen, toegangsdistributie, rijdistributie.
Een ander onderwerp dat in Postgres moet worden begrepen en dat ik helaas niet in mijn lezing kan behandelen, is MVCC. Ik heb een aparte presentatie over dit onderwerp op mijn website. En als je denkt dat deze presentatie moeilijk is: MVCC is waarschijnlijk mijn moeilijkste. En als je geïnteresseerd bent, kun je het bekijken op de website. Je kunt de video bekijken.
Een ander ding dat we moeten begrijpen zijn transactie-ID's. Veel transacties kunnen niet werken zonder unieke identificatiegegevens. En hier hebben we een uitleg van wat een transactie is. Postgres heeft twee transactienummeringssystemen. Ik weet dat dit geen erg mooie oplossing is.
Houd er ook rekening mee dat de dia's behoorlijk moeilijk te begrijpen zijn, dus waar u in het rood op moet letten, is waar u op moet letten.
Laten we eens kijken. Het transactienummer is rood gemarkeerd. De SELECT pg_back-functie wordt hier weergegeven. Het retourneert mijn transactie en de transactie-ID.
Nog één ding: als u deze presentatie leuk vindt en deze in uw database wilt uitvoeren, kunt u naar deze roze link gaan en de SQL voor deze presentatie downloaden. En u kunt het eenvoudig in uw PSQL uitvoeren en de hele presentatie staat onmiddellijk op uw scherm. Er zullen geen bloemen in zitten, maar we kunnen het tenminste zien.
In dit geval zien we het transactie-ID. Dit is het nummer dat we haar hebben toegewezen. En er is nog een ander type transactie-ID in Postgres, dat virtuele transactie-ID wordt genoemd
En wij moeten dit begrijpen. Dit is erg belangrijk, anders kunnen we de vergrendeling in Postgres niet begrijpen.
Een virtuele transactie-ID is een transactie-ID die geen persistente waarden bevat. Als ik bijvoorbeeld een SELECT-opdracht uitvoer, zal ik hoogstwaarschijnlijk de database niet wijzigen en niets vergrendelen. Dus als we een eenvoudige SELECT uitvoeren, geven we die transactie geen persistente ID. We geven haar daar alleen een virtuele ID.
En dit verbetert de prestaties van Postgres en verbetert de opschoonmogelijkheden, zodat de virtuele transactie-ID uit twee cijfers bestaat. Het eerste getal vóór de schuine streep is de backend-ID. En aan de rechterkant zien we alleen een teller.
Als ik daarom een verzoek uitvoer, staat er dat de backend-ID 2 is.
En als ik een reeks van dergelijke transacties uitvoer, zien we dat de teller elke keer dat ik een zoekopdracht uitvoer, toeneemt. Wanneer ik bijvoorbeeld de query 2/10, 2/11, 2/12, enz.
Houd er rekening mee dat er hier twee kolommen zijn. Aan de linkerkant zien we de virtuele transactie-ID – 2/12. En aan de rechterkant hebben we een permanente transactie-ID. En dit veld is leeg. En deze transactie wijzigt de database niet. Ik geef het dus geen permanente transactie-ID.
Zodra ik het analysecommando ((ANALYZE)) uitvoer, geeft dezelfde zoekopdracht mij een permanente transactie-ID. Kijk hoe dit voor ons is veranderd. Ik had dit ID voorheen niet, maar nu heb ik het.
Dus hier is nog een verzoek, nog een transactie. Het virtuele transactienummer is 2/13. En als ik om een persistente transactie-ID vraag, krijg ik die als ik de query uitvoer.
Dus nog een keer. We hebben een virtuele transactie-ID en een persistente transactie-ID. Begrijp dit punt gewoon om het gedrag van Postgre te begrijpen.
We gaan verder naar het derde deel. Hier lopen we eenvoudig door de verschillende soorten sluizen in Postgres. Het is niet erg interessant. Het laatste deel zal veel interessanter zijn. Maar we moeten rekening houden met de fundamentele zaken, omdat we anders niet zullen begrijpen wat er daarna zal gebeuren.
We zullen dit gedeelte doornemen en naar elk type slot kijken. En ik zal je voorbeelden laten zien van hoe ze worden geïnstalleerd, hoe ze werken, ik zal je enkele vragen laten zien die je kunt gebruiken om te zien hoe vergrendelen werkt in Postgres.
Om een query te maken en te zien wat er in Postgres gebeurt, moeten we de query uitvoeren in de systeemweergave. In dit geval wordt pg_lock rood gemarkeerd. Pg_lock is een systeemtabel die ons vertelt welke sloten momenteel in gebruik zijn in Postgres.
Het is echter erg moeilijk voor mij om u pg_lock zelf te laten zien, omdat het behoorlijk complex is. Daarom heb ik een weergave gemaakt die pg_locks toont. En het doet ook wat werk voor mij waardoor ik het beter kan begrijpen. Dat wil zeggen, het sluit mijn vergrendelingen, mijn eigen sessie, enz. uit. Het is gewoon standaard SQL en je kunt er beter mee laten zien wat er aan de hand is.
Een ander probleem is dat deze weergave erg breed is, dus ik moet een tweede maken: lockview2.
En het laat me meer kolommen uit de tabel zien. En nog eentje die me de rest van de kolommen laat zien. Dit is behoorlijk complex, dus ik heb geprobeerd het zo eenvoudig mogelijk weer te geven.
Daarom hebben we een tabel gemaakt met de naam Lockdemo. En we hebben daar één lijn gemaakt. Dit is onze voorbeeldtafel. En we gaan secties maken om u voorbeelden van sloten te laten zien.
Dus één rij, één kolom. Het eerste type slot heet ACCESS SHARE. Dit is de minst beperkende blokkering. Dit betekent dat het vrijwel niet conflicteert met andere sloten.
En als we expliciet een slot willen definiëren, voeren we het commando “lock table” uit. En het zal uiteraard blokkeren, dat wil zeggen dat we in de ACCESS SHARE-modus de vergrendeltabel starten. En als ik PSQL op de achtergrond uitvoer, start ik op deze manier de tweede sessie vanaf mijn eerste sessie. Dat wil zeggen: wat zal ik hier doen? Ik ga naar een andere sessie en zeg: "Laat me de lockview voor dit verzoek zien." En hier heb ik AccessShareLock in deze tabel. Dit is precies wat ik vroeg. En hij zegt dat het blok is toegewezen. Erg makkelijk.
Verder, als we naar de tweede kolom kijken, dan is daar niets. Ze zijn leeg.
En als ik de opdracht "SELECT" uitvoer, dan is dit de impliciete (expliciete) manier om AccessShareLock aan te vragen. Dus ik geef mijn tabel vrij en voer de query uit en de query retourneert meerdere rijen. En in een van de regels zien we AccessShareLock. Daarom roept SELECT AccessShareLock op tafel. En het is met vrijwel niets in conflict, omdat het een slot op laag niveau is.
Wat moet ik doen als ik een SELECT uitvoer en drie verschillende tabellen heb? Voorheen draaide ik slechts één tabel, nu gebruik ik er drie: pg_class, pg_namespace en pg_attribute.
En als ik nu naar de query kijk, zie ik 9 AccessShareLocks in drie tabellen. Waarom? Drie tabellen zijn blauw gemarkeerd: pg_attribute, pg_class, pg_namespace. Maar je kunt ook zien dat alle indexen die via deze tabellen worden gedefinieerd, ook AccessShareLock hebben.
En dit is een slot dat praktisch niet in conflict is met anderen. En het enige dat het doet, is simpelweg voorkomen dat we de tabel opnieuw instellen terwijl we deze selecteren. Het is logisch. Dat wil zeggen, als we een tabel selecteren, verdwijnt deze op dat moment, dan is dit verkeerd, dus AccessShare is een vergrendeling op laag niveau die ons vertelt "laat deze tabel niet vallen terwijl ik aan het werk ben". In wezen is dat alles wat ze doet.
ROW SHARE - Dit slot is een beetje anders.
Laten we een voorbeeld nemen. SELECT ROW SHARE-methode om elke rij afzonderlijk te vergrendelen. Zo kan niemand ze verwijderen of wijzigen terwijl wij ze bekijken.
Dus wat doet SHARE LOCK? We zien dat de transactie-ID 681 is voor SELECT. En dit is interessant. Wat is hier gebeurd? De eerste keer dat we het nummer zien, is in het veld ‘Vergrendelen’. We nemen de transactie-ID en er staat dat deze in de exclusieve modus wordt geblokkeerd. Het enige wat het doet is dat er staat dat ik een rij heb die technisch ergens in de tabel is vergrendeld. Maar hij zegt niet waar precies. We zullen dit later in meer detail bekijken.
Hier zeggen we dat het slot door ons wordt gebruikt.
Een exclusief slot zegt dus expliciet dat het exclusief is. En ook als u een rij in deze tabel verwijdert, zal dit gebeuren, zoals u kunt zien.
SHARE EXCLUSIVE is een langer slot.
Dit is de (ANALYZE) analyseopdracht die zal worden gebruikt.
DEELVERGRENDELING – u kunt expliciet vergrendelen in de deelmodus.
U kunt ook een unieke index maken. En daar zie je SHARE LOCK, dat er deel van uitmaakt. En het vergrendelt de tafel en plaatst er een SHARE LOCK op.
Standaard betekent SHARE LOCK op een tafel dat andere mensen de tafel kunnen lezen, maar niemand deze kan wijzigen. En dit is precies wat er gebeurt als u een unieke index maakt.
Als ik een unieke gelijktijdige index maak, heb ik een ander type vergrendeling omdat, zoals u zich herinnert, het gebruik van gelijktijdige indexen de vergrendelingsvereiste vermindert. En als ik een normaal slot gebruik, een normale index, dan voorkom ik dat er naar de tabelindex wordt geschreven terwijl deze wordt gemaakt. Als ik een gelijktijdige index gebruik, moet ik een ander type vergrendeling gebruiken.
SHARE ROW EXCLUSIEF – dit kan opnieuw expliciet (expliciet) worden ingesteld.
Of we kunnen een regel maken, d.w.z. een specifiek geval nemen waarin deze zal worden gebruikt.
EXCLUSIEVE vergrendeling betekent dat niemand anders de tafel kan verwisselen.
Hier zien we verschillende soorten sloten.
ACCESS EXCLUSIVE is bijvoorbeeld een blokkeeropdracht. Bijvoorbeeld als je dat doet CLUSTER table
, dan betekent dit dat niemand daar kan schrijven. En het vergrendelt niet alleen de tabel zelf, maar ook de indexen.
Dit is de tweede pagina van de ACCESS EXCLUSIVE-blokkering, waar we precies zien wat deze in de tabel blokkeert. Het vergrendelt individuele tabelrijen, wat best interessant is.
Dat is alle basisinformatie die ik wilde geven. We hadden het over sloten, over transactie-ID's, we hadden het over virtuele transactie-ID's, over permanente transactie-ID's.
En nu zullen we enkele blokkeringsvoorbeelden doornemen. Dit is het meest interessante deel. We zullen naar zeer interessante gevallen kijken. En mijn doel in deze presentatie is om u een beter begrip te geven van wat Postgres feitelijk doet als het bepaalde dingen probeert te blokkeren. Ik denk dat hij heel goed is in het blokkeren van onderdelen.
Laten we eens kijken naar enkele specifieke voorbeelden.
We beginnen met tabellen en één rij in een tabel. Wanneer ik iets invoeg, worden ExclusiveLock, Transactie-ID en ExclusiveLock weergegeven op de tafel.
Wat gebeurt er als ik nog twee rijen invoeg? En nu heeft onze tafel drie rijen. En ik heb één rij ingevoegd en kreeg dit als uitvoer. En als ik nog twee rijen invoeg, wat is daar dan raar aan? Er is hier iets raars, want ik heb drie rijen aan deze tabel toegevoegd, maar ik heb nog steeds twee rijen in de slottabel. En dit is in essentie het fundamentele gedrag van Postgres.
Veel mensen denken dat als je in een database 100 rijen vergrendelt, je ook 100 vergrendelitems moet aanmaken. Als ik 1 rijen tegelijk blokkeer, heb ik 000 van dergelijke zoekopdrachten nodig. En als ik een miljoen of een miljard nodig heb om te blokkeren. Maar als we dit doen, zal het niet zo goed werken. Als je een systeem hebt gebruikt dat voor elke afzonderlijke rij blokkeringsgegevens aanmaakt, dan zie je dat dit ingewikkeld is. Omdat je onmiddellijk een lock-tabel moet definiëren die kan overlopen, maar Postgres doet dat niet.
En wat echt belangrijk is aan deze dia is dat het duidelijk aantoont dat er een ander systeem binnen MVCC draait dat individuele rijen vergrendelt. Dus als je miljarden rijen vergrendelt, maakt Postgres geen miljard afzonderlijke vergrendelingsopdrachten. En dit heeft een zeer goed effect op de productiviteit.
Hoe zit het met een update? Ik ben de rij nu aan het bijwerken en je kunt zien dat er twee verschillende bewerkingen tegelijk zijn uitgevoerd. Het vergrendelde tegelijkertijd de tabel, maar ook de index. En hij moest de index vergrendelen omdat er unieke beperkingen zijn voor deze tabel. En we willen ervoor zorgen dat niemand het verandert, dus blokkeren we het.
Wat gebeurt er als ik twee rijen wil updaten? En we zien dat hij zich op dezelfde manier gedraagt. We doen twee keer zoveel updates, maar precies hetzelfde aantal vergrendelregels.
Als je je afvraagt hoe Postgres dit doet, moet je naar mijn toespraken op MVCC luisteren om te leren hoe Postgres deze regels intern markeert als het verandert. En Postgres heeft een manier waarop het dit doet, maar het doet het niet op tafelvergrendelingsniveau, het doet het op een lager en efficiënter niveau.
Wat moet ik doen als ik iets wil verwijderen? Als ik bijvoorbeeld één rij verwijder en ik heb nog steeds mijn twee blokkerende invoergegevens, en zelfs als ik ze allemaal wil verwijderen, zijn ze er nog steeds.
En ik wil bijvoorbeeld 1 regels invoegen en dan 000 regels verwijderen of toevoegen. Vervolgens worden de afzonderlijke regels die ik toevoeg of wijzig, hier niet opgenomen. Ze zijn op een lager niveau binnen de serie zelf geschreven. En tijdens de MVCC-toespraak heb ik hier uitvoerig over gesproken. Maar als u vergrendelingen analyseert, is het erg belangrijk om er zeker van te zijn dat u op tabelniveau vergrendelt en dat u niet ziet hoe individuele rijen hier worden vastgelegd.
Hoe zit het met expliciete blokkering?
Als ik op vernieuwen klik, zijn er twee rijen vergrendeld. En als ik ze allemaal selecteer en op ‘overal bijwerken’ klik, heb ik nog steeds twee blokkerende records.
We maken geen afzonderlijke records voor elke afzonderlijke rij. Omdat de productiviteit dan daalt, kan er teveel van zijn. En het kan zijn dat we in een onaangename situatie terechtkomen.
En hetzelfde: als we het delen, kunnen we het allemaal 30 keer doen.
We herstellen onze tabel, verwijderen alles en voegen vervolgens opnieuw één rij in.
Een ander gedrag dat u in Postgres ziet en dat zeer bekend en gewenst gedrag is, is dat u een update of selectie kunt uitvoeren. En je kunt dit tegelijkertijd doen. En selecteer blokkeert de update niet en hetzelfde in de tegenovergestelde richting. We vertellen de lezer dat hij de schrijver niet moet blokkeren, en de schrijver heeft de lezer niet geblokkeerd.
Ik zal je hiervan een voorbeeld laten zien. Ik ga nu een keuze maken. We zullen dan de INSERT doen. En dan zie je - 694. Je kunt de ID zien van de transactie die deze invoeging heeft uitgevoerd. En zo werkt het.
En als ik nu naar mijn backend-ID kijk, is het nu 695.
En ik zie 695 in mijn tabel verschijnen.
En als ik hier zo update, krijg ik een ander geval. In dit geval is 695 een exclusieve vergrendeling en vertoont de update hetzelfde gedrag, maar er is geen conflict tussen beide, wat vrij ongebruikelijk is.
En je kunt zien dat het bovenaan ShareLock is, en onderaan ExclusiveLock. En beide transacties zijn gelukt.
En je moet naar mijn lezing op MVCC luisteren om te begrijpen hoe dit gebeurt. Maar dit is een illustratie dat u het tegelijkertijd kunt doen, d.w.z. tegelijkertijd een SELECT en een UPDATE uitvoeren.
Laten we resetten en nog een handeling uitvoeren.
Als u twee updates tegelijkertijd op dezelfde rij probeert uit te voeren, wordt deze geblokkeerd. En onthoud, ik zei dat de lezer de schrijver niet blokkeert, en de schrijver blokkeert de lezer niet, maar de ene schrijver blokkeert de andere schrijver. Dat wil zeggen dat we niet twee mensen tegelijkertijd dezelfde rij kunnen laten bijwerken. Je moet wachten tot een van hen klaar is.
En om dit te illustreren, zal ik naar de Lockdemo-tabel kijken. En we kijken naar één rij. Per transactie 698.
We hebben dit bijgewerkt naar 2. 699 is de eerste update. En de transactie is succesvol verlopen of bevindt zich in een lopende transactie en wacht op bevestiging of annulering door ons.
Maar kijk eens naar iets anders: 2/51 is onze eerste transactie, onze eerste sessie. 3/112 is het tweede verzoek dat van boven kwam en de waarde veranderde in 3. En als je het opmerkt, de bovenste heeft zichzelf vergrendeld, wat 699 is. Maar 3/112 heeft het slot niet toegekend. De kolom Lock_mode geeft aan waar hij op wacht. Het verwacht 699. En als je kijkt naar waar 699 is, is het hoger. En wat heeft de eerste sessie opgeleverd? Ze creëerde een exclusieve vergrendeling op haar eigen transactie-ID. Dit is hoe Postgres het doet. Het blokkeert zijn eigen transactie-ID. En als u wilt wachten totdat iemand de transactie bevestigt of annuleert, moet u wachten terwijl er een transactie in behandeling is. En daarom kunnen we een vreemde lijn zien.
Laten we nog eens kijken. Aan de linkerkant zien we onze verwerkings-ID. In de tweede kolom zien we onze virtuele transactie-ID en in de derde zien we lock_type. Wat betekent dit? Wat het in wezen zegt, is dat het de transactie-ID blokkeert. Maar merk op dat alle rijen onderaan relatie zeggen. En zo heb je dus twee soorten sloten op tafel. Er is een relatieslot. En dan is er nog de transactionid-blokkering, waarbij je zelf blokkeert, wat precies is wat er gebeurt op de eerste rij of helemaal onderaan, waar de transactionid staat, waar we wachten tot 699 klaar is met zijn bewerking.
Ik zal zien wat hier gebeurt. En hier gebeuren twee dingen tegelijkertijd. U kijkt naar een transactie-ID-slot in de eerste rij dat zichzelf vergrendelt. En ze blokkeert zichzelf om mensen te laten wachten.
Als je naar de 6e regel kijkt, is dit dezelfde invoer als de eerste. En daarom is transactie 699 geblokkeerd. 700 is ook zelfremmend. En dan zie je in de onderste rij dat we wachten tot 699 klaar is met werken.
En in lock_type, tuple zie je cijfers.
Je kunt zien dat het 0/10 is. En dit is het paginanummer, en ook de offset van deze specifieke rij.
En je ziet dat het 0/11 wordt als we updaten.
Maar in werkelijkheid is het 0/10, omdat er op deze operatie wordt gewacht. We hebben de kans om te zien dat dit de serie is waarvan ik wacht om deze te bevestigen.
Zodra we het hebben bevestigd en op commit hebben gedrukt, en wanneer de update is voltooid, krijgen we dit weer. Transactie 700 is het enige slot, het wacht niet op iemand anders omdat het is vastgelegd. Het wacht eenvoudigweg tot de transactie is voltooid. Zodra 699 op is, wachten we nergens meer op. En nu zegt transactie 700 dat alles in orde is, dat alle benodigde vergrendelingen op alle toegestane tafels aanwezig zijn.
En om dit geheel nog ingewikkelder te maken, creëren we een ander beeld, dat ons deze keer een hiërarchie zal bieden. Ik verwacht niet dat u dit verzoek begrijpt. Maar dit geeft ons een duidelijker beeld van wat er aan de hand is.
Dit is een recursieve weergave die ook een andere sectie heeft. En dan brengt het alles weer samen. Laten we dit gebruiken.
Wat als we drie gelijktijdige updates uitvoeren en zeggen dat de rij nu drie is? En we veranderen 3 naar 4.
En hier zien we er 4. En transactie-ID 702.
En dan verander ik 4 in 5. En 5 in 6, en 6 in 7. En ik zal een aantal mensen op een rij zetten die zullen wachten tot deze ene transactie eindigt.
En alles wordt duidelijk. Wat is de eerste rij? Dit is 702. Dit is de transactie-ID waarmee deze waarde oorspronkelijk is ingesteld. Wat staat er in mijn kolom Toegekend? Ik heb merktekens f
. Dit zijn mijn updates die (5, 6, 7) niet kunnen worden goedgekeurd omdat we wachten tot transactie-ID 702 eindigt. Daar hebben we transactie-ID-blokkering. En dit resulteert in 5 transactionele ID-sloten.
En als je naar 704 kijkt, naar 705, daar is nog niets geschreven, omdat ze nog niet weten wat er aan de hand is. Ze schrijven eenvoudigweg dat ze geen idee hebben wat er gebeurt. En ze gaan gewoon slapen omdat ze wachten tot iemand klaar is en wakker wordt als er een mogelijkheid is om van rij te wisselen.
Dit is hoe het eruit ziet. Het is duidelijk dat ze allemaal wachten op de 12e regel.
Dit is wat we hier zagen. Hier is 0/12.
Dus zodra de eerste transactie is goedgekeurd, kunt u hier zien hoe de hiërarchie werkt. En nu wordt alles duidelijk. Ze worden allemaal schoon. En eigenlijk wachten ze nog steeds.
Dit is wat er gebeurt. 702 toezeggingen. En nu krijgt 703 deze rijvergrendeling, en dan begint 704 te wachten tot 703 zich vastlegt. En daar zit de 705 ook op te wachten. En als dit allemaal is voltooid, ruimen ze zichzelf op. En ik wil erop wijzen dat iedereen in de rij staat. En dit lijkt erg op een situatie in de file, waarbij iedereen op de eerste auto wacht. De eerste auto stopt en iedereen gaat in een lange rij staan. Dan beweegt hij, dan kan de volgende auto vooruit rijden en zijn blok pakken, enz.
En als dit u nog niet ingewikkeld genoeg leek, dan zullen we het nu met u hebben over impasses. Ik weet niet wie van jullie ze zijn tegengekomen. Dit is een vrij algemeen probleem in databasesystemen. Maar er is sprake van een impasse wanneer de ene sessie wacht tot een andere sessie iets doet. En op dit moment wacht een andere sessie op de eerste sessie om iets te doen.
En als Ivan bijvoorbeeld zegt: “Geef mij iets”, en ik zeg: “Nee, ik geef het jou alleen als jij mij iets anders geeft.” En hij zegt: “Nee, ik zal het je niet geven als jij het mij niet geeft.” En we komen in een impasse terecht. Ik ben er zeker van dat Ivan dit niet zal doen, maar je begrijpt de betekenis dat we twee mensen hebben die iets willen krijgen en ze zijn niet bereid het weg te geven totdat de andere persoon ze geeft wat ze willen. En er is geen oplossing.
En in wezen moet uw database dit detecteren. En dan moet je een van de sessies verwijderen of sluiten, want anders blijven ze daar voor altijd staan. En we zien het in databases, we zien het in besturingssystemen. En op alle plaatsen waar we parallelle processen hebben, kan dit gebeuren.
En nu zullen we twee impasses installeren. We zetten 50 en 80. In de eerste rij update ik van 50 naar 50. Ik krijg transactienummer 710.
En dan verander ik 80 in 81, en 50 in 51.
En dit is hoe het eruit zal zien. En dus heeft 710 een rij geblokkeerd en wacht 711 op bevestiging. We hebben dit gezien toen we de update uitvoerden. 710 is de eigenaar van onze serie. En 711 wacht tot 710 de transactie heeft voltooid.
En er staat zelfs op welke rij de impasses plaatsvinden. En hier begint het raar te worden.
Nu updaten we 80 naar 80.
En dit is waar de impasses beginnen. 710 wacht op een antwoord van 711, en 711 wacht op 710. En dit zal niet goed aflopen. En er is geen uitweg hieruit. En ze verwachten een reactie van elkaar.
En het zal alles gewoon gaan vertragen. En dat willen wij niet.
En Postgres heeft manieren om op te merken wanneer dit gebeurt. En wanneer dit gebeurt, krijgt u deze foutmelding. En hieruit wordt duidelijk dat dit en dat proces wacht op een SHARE LOCK van een ander proces, d.w.z. dat wordt geblokkeerd door het 711-proces. En dat proces wachtte op het geven van een SHARE LOCK op die en die transactie-ID en werd geblokkeerd door dat en die proces. Er is hier dus sprake van een impasse.
Zijn er drievoudige impasses? Is dit mogelijk? Ja.
Deze getallen voeren we in een tabel in. We veranderen 40 naar 40, we blokkeren.
We veranderen 60 naar 61, 80 naar 81.
En dan veranderen we 80 en dan boem!
En 714 wacht nu op 715. De 716 wacht op de 715. En er kan niets aan gedaan worden.
Er zijn hier niet langer twee mensen, er zijn hier al drie mensen. Ik wil iets van jou, deze wil iets van een derde persoon, en de derde persoon wil iets van mij. En we eindigen in een driewegwacht omdat we allemaal wachten tot de andere persoon voltooit wat hij of zij moet doen.
En Postgres weet op welke rij dit gebeurt. En dus krijg je het volgende bericht, waaruit blijkt dat je een probleem hebt waarbij drie ingangen elkaar blokkeren. En er zijn hier geen beperkingen. Dit kan het geval zijn wanneer 20 vermeldingen elkaar blokkeren.
Het volgende probleem is serialiseerbaar.
Indien speciaal serialiseerbaar slot.
En we keren terug naar 719. De output is vrij normaal.
En u kunt klikken om de transactie serialiseerbaar te maken.
En je realiseert je dat je nu een ander soort SA-slot hebt: het betekent serialiseerbaar.
En dus hebben we een nieuw type slot genaamd SARieadLock, een serieel slot waarmee u serienummers kunt invoeren.
En u kunt ook unieke indexen invoegen.
In deze tabel hebben we unieke indexen.
Dus als ik hier het getal 2 invul, heb ik een 2. Maar helemaal bovenaan zet ik er nog een 2 in. En je ziet dat 721 een exclusief slot heeft. Maar nu wacht 722 tot 721 zijn bewerking heeft voltooid, omdat het pas 2 kan invoegen als het weet wat er met 721 zal gebeuren.
En als we een subtransactie doen.
Hier hebben we 723.
En als we het punt opslaan en vervolgens bijwerken, krijgen we een nieuwe transactie-ID. Dit is een ander gedragspatroon waar u zich bewust van moet zijn. Als we dit retourneren, verdwijnt het transactie-ID. 724 vertrekt. Maar nu hebben we er 725.
Dus wat probeer ik hier te doen? Ik probeer u voorbeelden te laten zien van ongebruikelijke sloten die u mogelijk tegenkomt: of het nu serialiseerbare sloten of SAVEPOINT zijn, dit zijn verschillende soorten sloten die in de slotentabel zullen verschijnen.
Dit is het creëren van expliciete (expliciete) vergrendelingen, die pg_advisory_lock hebben.
En u ziet dat het blokkeringstype als adviserend wordt vermeld. En hier staat in het rood ‘advies’. En je kunt tegelijkertijd op deze manier blokkeren met pg_advisory_unlock.
En ter afsluiting zou ik u nog iets verbluffends willen laten zien. Ik zal een nieuwe weergave maken. Maar ik zal de pg_locks-tabel samenvoegen met de pg_stat_activity-tabel. En waarom wil ik dit doen? Omdat ik hierdoor alle huidige sessies kan bekijken en precies kan zien op wat voor soort sloten ze wachten. En dit is best interessant als we de lock-tabel en de query-tabel samenvoegen.
En hier creëren we pg_stat_view.
En we werken de rij met één bij. En hier zien we 724. En dan updaten we onze rij naar drie. En wat zie je hier nu? Dit zijn verzoeken, dat wil zeggen dat u de volledige lijst met verzoeken ziet die in de linkerkolom staan. En dan zie je aan de rechterkant de blokkades en wat ze veroorzaken. En het kan voor u duidelijker zijn, zodat u niet elke keer opnieuw naar elke sessie hoeft te gaan om te kijken of u eraan moet deelnemen of niet. Zij doen het voor ons.
Een andere functie die erg handig is, is pg_blocking_pids
. Waarschijnlijk heb je nog nooit van haar gehoord. Wat doet ze? Hiermee kunnen we voor deze sessie 11740 vertellen op welke specifieke proces-ID's het wacht. En je kunt zien dat 11740 wacht op 724. En 724 staat helemaal bovenaan. En 11306 is uw proces-ID. In wezen loopt deze functie via uw vergrendeltabel. En ik weet dat het een beetje ingewikkeld is, maar jij slaagt erin het te begrijpen. In wezen doorloopt deze functie deze vergrendelingstabel en probeert te achterhalen waar dit proces-ID de vergrendelingen krijgt waarop het wacht. En het probeert ook te achterhalen welk proces-ID het proces heeft dat op de vergrendeling wacht. U kunt deze functie dus uitvoeren pg_blocking_pids
.
En dit kan erg handig zijn. We hebben dit pas in versie 9.6 toegevoegd, dus deze functie is pas 5 jaar oud, maar het is heel erg handig. En hetzelfde geldt voor het tweede verzoek. Het laat precies zien wat we moeten zien.
Dit is waar ik met je over wilde praten. En zoals ik had verwacht, hebben we al onze tijd opgebruikt omdat er zoveel glijbanen waren. En de dia's zijn beschikbaar om te downloaden. Ik wil je bedanken dat je hier bent. Ik weet zeker dat u zult genieten van de rest van de conferentie. Hartelijk dank!
Vragen:
Als ik bijvoorbeeld rijen probeer bij te werken, en de tweede sessie probeert de hele tabel te verwijderen. Voor zover ik het begrijp zou er zoiets als een intent lock moeten zijn. Bestaat er zoiets in Postgres?
Laten we teruggaan naar het allereerste begin. U herinnert zich misschien dat wanneer u iets doet, bijvoorbeeld wanneer u een SELECT doet, wij een AccessShareLock uitgeven. En dit voorkomt dat de tafel valt. Wil je dus bijvoorbeeld een rij in een tabel bijwerken of een rij verwijderen, dan kan iemand niet tegelijkertijd de hele tabel verwijderen omdat je deze AccessShareLock over de hele tabel en over de rij houdt. En als je klaar bent, kunnen ze het verwijderen. Maar terwijl je daar direct iets verandert, zullen ze het niet kunnen doen.
Laten we het nog eens doen. Laten we verder gaan met het verwijderingsvoorbeeld. En je ziet hoe er een exclusief slot op de rij boven de hele tafel zit.
Dit ziet eruit als exclusief voor slot, toch?
Ja, het lijkt erop. Ik begrijp waar je het over hebt. Je zegt dat als ik een SELECT doe, ik een ShareExclusive heb en deze vervolgens Row Exclusive maak, wordt dat een probleem? Maar verrassend genoeg vormt dit geen probleem. Dit lijkt op het vergroten van de vergrendelingsgraad, maar in wezen heb ik een vergrendeling die verwijdering verhindert. En nu, als ik dit slot krachtiger maak, voorkomt het nog steeds verwijdering. Het is dus niet zo dat ik omhoog ga. Dat wil zeggen, het verhinderde dat dit ook gebeurde toen het zich op een lager niveau bevond, dus als ik het niveau verhoog, voorkomt het nog steeds dat de tabel wordt verwijderd.
Ik begrijp waar je het over hebt. Er is hier geen sprake van escalatie van een slot, waarbij u één slot probeert op te geven om een sterker slot te introduceren. Hier verhoogt het deze preventie alleen maar over de hele linie, zodat het geen enkel conflict veroorzaakt. Maar het is een goede vraag. Hartelijk dank dat u dit vraagt!
Wat moeten we doen om een impasse te voorkomen als we veel sessies en een groot aantal gebruikers hebben?
Postgres merkt automatisch impassesituaties op. En het zal automatisch een van de sessies verwijderen. De enige manier om dode blokkering te voorkomen, is door mensen in dezelfde volgorde te blokkeren. Dus als je naar je applicatie kijkt, is dit vaak de reden voor impasses... Laten we ons voorstellen dat ik twee verschillende dingen wil blokkeren. Eén applicatie vergrendelt tabel 1, een andere applicatie vergrendelt tabel 2, en vervolgens tabel 1. En de eenvoudigste manier om impasses te voorkomen is door naar uw applicatie te kijken en ervoor te zorgen dat de vergrendeling in alle applicaties in dezelfde volgorde plaatsvindt. En dit elimineert meestal 80% van de problemen, omdat allerlei soorten mensen deze applicaties schrijven. En als je ze in dezelfde volgorde blokkeert, kom je niet in een impasse terecht.
Hartelijk dank voor uw optreden! U had het over vacuüm vol en, als ik het goed begrijp, verstoort vacuüm vol de volgorde van de records in afzonderlijke opslag, zodat de huidige records ongewijzigd blijven. Waarom vereist vacuüm vol exclusieve vergrendelingstoegang en waarom is dit in strijd met schrijfbewerkingen?
Dat is een goede vraag. De reden is dat het vacuüm vol de tafel in beslag neemt. En we creëren feitelijk een nieuwe versie van de tabel. En de tafel zal nieuw zijn. Het blijkt dat dit een compleet nieuwe versie van de tabel zal zijn. En het probleem is dat wanneer we dit doen, we niet willen dat mensen het lezen, omdat we ze nodig hebben om de nieuwe tabel te zien. En dit sluit dus aan bij de vorige vraag. Als we tegelijkertijd zouden kunnen lezen, zouden we het niet kunnen verplaatsen en mensen naar een nieuwe tafel kunnen leiden. We zouden moeten wachten tot iedereen klaar is met het lezen van deze tabel, en dus is het in wezen een slot-exclusieve situatie.
We zeggen alleen dat we vanaf het begin vergrendelen, omdat we weten dat we helemaal aan het einde een exclusief slot nodig hebben om iedereen naar de nieuwe kopie te verplaatsen. Wij kunnen dit dus mogelijk oplossen. En we doen het op deze manier met gelijktijdige indexering. Maar dit is veel moeilijker om te doen. En dit heeft sterk betrekking op uw vorige vraag over exclusief slot.
Is het mogelijk om een time-out voor vergrendeling toe te voegen aan Postgres? In Oracle kan ik bijvoorbeeld "selecteer om te updaten" schrijven en 50 seconden wachten voordat ik update. Het was goed voor de toepassing. Maar in Postgres moet ik het óf meteen doen en helemaal niet wachten, óf wachten tot een tijdje.
Ja, u kunt een time-out kiezen voor uw sloten, op uw sloten. Je kunt ook een 'no way'-opdracht geven, wat... als je het slot niet onmiddellijk kunt verkrijgen. Daarom is er een time-out voor de vergrendeling of iets anders waarmee u dit kunt doen. Dit gebeurt niet op syntactisch niveau. Dit gebeurt als een variabele op de server. Soms kan hiervan geen gebruik worden gemaakt.
Kunt u dia 75 openen?
Да.
En mijn vraag is de volgende. Waarom verwachten beide updateprocessen 703?
En dit is een geweldige vraag. Ik begrijp overigens niet waarom Postgres dit doet. Maar toen 703 werd gemaakt, verwachtte het 702. En toen 704 en 705 verschijnen, lijkt het alsof ze niet weten wat ze verwachten, omdat er nog niets is. En Postgres doet het op deze manier: als je geen slot kunt krijgen, schrijft het: "Wat heeft het voor zin om jou te verwerken?", Omdat je al op iemand wacht. Dus laten we het gewoon in de lucht hangen, het zal het helemaal niet bijwerken. Maar wat gebeurde hier? Zodra 702 het proces had voltooid en 703 de vergrendeling had ontvangen, keerde het systeem terug. En ze zei dat we nu twee mensen hebben die wachten. En laten we ze dan samen bijwerken. En laten we aangeven dat ze allebei in verwachting zijn.
Ik weet niet waarom Postgres dit doet. Maar er is een probleem genaamd f…. Het lijkt mij dat dit geen term in het Russisch is. Dit is wanneer iedereen op één kasteel wacht, ook al zijn er twintig autoriteiten die op het kasteel wachten. En plotseling worden ze allemaal tegelijk wakker. En iedereen begint te reageren. Maar het systeem zorgt ervoor dat iedereen op 20 wacht. Omdat ze allemaal wachten, zetten we ze meteen allemaal op een rij. En als er nog een nieuw verzoek verschijnt dat hierna is gegenereerd, bijvoorbeeld 703, dan zal er weer een leegte zijn.
En het lijkt mij dat dit wordt gedaan zodat we kunnen zeggen dat in dit stadium 702 wacht op 703, en dat al degenen die daarna komen geen toegang hebben tot dit veld. Maar zodra de eerste ober vertrekt, krijgen alle mensen die op dat moment vóór de update aan het wachten waren, hetzelfde token. En dus denk ik dat dit wordt gedaan zodat we het op volgorde kunnen verwerken, zodat ze op de juiste manier worden besteld.
Ik heb dit altijd als een nogal vreemd fenomeen beschouwd. Omdat we ze hier bijvoorbeeld helemaal niet vermelden. Maar het lijkt mij dat we elke keer dat we een nieuw slot geven, kijken naar al degenen die in afwachting zijn. Dan zetten we ze allemaal op een rij. En dan komt elke nieuwe die binnenkomt pas in de wachtrij als de volgende persoon klaar is met verwerken. Zeer goede vraag. Hartelijk dank voor uw vraag!
Het lijkt mij dat het veel logischer is als 705 704 verwacht.
Maar het probleem hier is het volgende. Technisch gezien kun je het een of het ander wakker maken. En dus zullen we de een of de ander wakker maken. Maar wat gebeurt er in het systeem? Je kunt zien hoe 703 helemaal bovenaan zijn eigen transactie-ID heeft geblokkeerd. Dit is hoe Postgres werkt. En 703 wordt geblokkeerd door zijn eigen transactie-ID, dus als iemand wil wachten, wacht hij op 703. En in wezen is 703 voltooid. En pas na de voltooiing ervan ontwaakt een van de processen. En we weten niet wat dit proces precies zal zijn. Daarna verwerken we alles stapsgewijs. Maar het is niet duidelijk welk proces het eerst wordt geactiveerd, omdat het elk van deze processen kan zijn. In wezen hadden we een planner die zei dat we nu al deze processen kunnen activeren. We kiezen er willekeurig één uit. We moeten ze dus allebei noteren, omdat we ze allebei kunnen ontwaken.
En het probleem is dat we CP-oneindigheid hebben. En daarom is het zeer waarschijnlijk dat we de laatste wakker kunnen maken. En als we bijvoorbeeld de laatste wakker maken, wachten we op degene die zojuist de blokkering heeft ontvangen, dus we bepalen niet wie er precies als eerste wakker zal worden. We creëren eenvoudigweg zo’n situatie, en het systeem zal ze in willekeurige volgorde wakker maken.
Er is
Bron: www.habr.com