Avkodning av Bruce Momjian Ärsrapport 2020 "Unlocking the Postgres Lock Manager".

(Observera: Du kan fÄ alla SQL-frÄgor frÄn bilderna via denna lÀnk: )
Hej! Det Àr underbart att vara hÀr i Ryssland igen. Jag ber om ursÀkt för att jag inte kunde komma förra Äret, men i Är har Ivan och jag stora planer. Jag hoppas att jag kommer att vara hÀr mycket oftare. Jag Àlskar att besöka Ryssland. Jag kommer att besöka Tyumen och Tver. Jag Àr mycket glad att jag fÄr möjlighet att besöka dessa stÀder.
Jag heter Bruce Momjian. Jag arbetar pÄ EnterpriseDB och har jobbat med Postgres i över 23 Är. Jag bor i Philadelphia, USA. Jag reser ungefÀr 90 dagar om Äret och deltar i cirka 40 konferenser. Min , som innehÄller de bilder jag kommer att visa er nu. SÄ efter konferensen kan ni ladda ner dem frÄn min privata webbplats. DÀr finns ocksÄ omkring 30 presentationer. Dessutom finns det videor och ett stort antal bloggposter, över 500. Det Àr en ganska innehÄllsrik resurs. Och om ni Àr intresserade av detta material inbjuder jag er att anvÀnda det.
Tidigare var jag lÀrare, professor innan jag började arbeta med Postgres. Och jag Àr mycket glad att jag nu fÄr berÀtta för er vad jag planerar att berÀtta. Det hÀr Àr en av mina mest intressanta presentationer. Och denna presentation innehÄller 110 bilder. Vi kommer att börja med enkla saker, och mot slutet kommer redovisningen att bli alltmer komplex och ganska svÄr.

Det hÀr Àr en ganska obehaglig diskussion. Blockering Àr inte det mest populÀra Àmnet. Vi vill att det ska försvinna. Det Àr som att gÄ till tandlÀkaren.

- Blockering Àr ett problem för mÄnga som arbetar med databaser och har flera processer som körs samtidigt. De behöver blockering. Det vill sÀga, idag kommer jag att ge er grundlÀggande kunskaper om blockering.
- Transaktions-ID:n. Det hÀr Àr en ganska trÄkig del av presentationen, men den mÄste förstÄs.
- DÀrefter kommer vi att prata om blockeringstyper. Det hÀr Àr en ganska mekanisk del.
- Och dÀrefter kommer vi att ge nÄgra exempel pÄ blockeringar. Och det kommer att vara ganska svÄrt att ta till sig.

LÄt oss prata om blockeringar.

Terminologin Àr ganska komplex hos oss. Hur mÄnga av er vet var denna passage kommer ifrÄn? TvÄ personer. Det Àr frÄn spelet som heter "Kolosalt Àventyr i grottan". Det var ett textbaserat datorspel pÄ 80-talet, tror jag. Man behövde gÄ in i en grotta, i en labyrint och texten Àndrades, men innehÄllet var ungefÀr detsamma varje gÄng. SÄ hÀr minns jag spelet.

Och hÀr ser vi namnen pÄ lÄsningar som kommit till oss frÄn Oracle. Vi anvÀnder dem.

HĂ€r ser vi termer som förvirrar mig. Till exempel, SHARE UPDATE EXCLUSIVE. Sedan SHARE RAW EXCLUSIVE. Ărligt talat Ă€r dessa namn inte sĂ€rskilt tydliga. Vi kommer att försöka titta nĂ€rmare pĂ„ dem. Vissa innehĂ„ller ordet "share", vilket betyder â att separera. Vissa innehĂ„ller ordet "exclusive" â exklusiv. I vissa finns bĂ„da dessa ord. Jag skulle vilja börja med hur dessa lĂ„sningar fungerar.

Och ocksĂ„ Ă€r ordet "access" mycket viktigt. Och ordet "row" â rad. Det vill sĂ€ga fördelning av Ă„tkomst, fördelning av rader.

Ett annat problem som mĂ„ste förstĂ„s i Postgres, jag kan tyvĂ€rr inte berĂ€tta om det i min presentation, Ă€r MVCC. Jag har en separat presentation om detta Ă€mne pĂ„ min webbplats. Och om du tror att denna presentation Ă€r svĂ„r, sĂ„ Ă€r MVCC â troligen min mest komplexa. Och om du Ă€r intresserad kan du titta pĂ„ den pĂ„ webbplatsen. Du kan se videon.

En annan punkt som vi mĂ„ste förstĂ„ â Ă€r transaktionsidentifierare. MĂ„nga transaktioner kan inte fungera utan unika identifierare. Och hĂ€r har vi en förklaring av vad en transaktion Ă€r. I Postgres finns det tvĂ„ system för numrering av transaktioner. Jag vet, det Ă€r ingen vacker lösning.

TÀnk ocksÄ pÄ att bilderna kommer att vara ganska svÄra att förstÄ, sÄ det som Àr framhÀvt i röd fÀrg, det Àr just det som du bör uppmÀrksamma.

Vi ser. Nummeret för transaktionen Àr framhÀvt i röd fÀrg. HÀr visas funktionen SELECT pg_back. Den returnerar min transaktion och ID för denna transaktion.
En annan sak, om du gillar den hÀr presentationen och vill köra den i din databas, kan du följa den rosa markerade lÀnken och ladda ner SQL för den hÀr presentationen. Och du kan enkelt köra den i din PSQL sÄ kommer hela presentationen att dyka upp pÄ din skÀrm omedelbart. Den kommer inte att innehÄlla fÀrger, men i alla fall kan vi se den.

I det hÀr fallet ser vi transaktions-ID:t. Det Àr numret som vi tilldelade den. Det finns ocksÄ en annan typ av transaktions-ID i Postgres som kallas virtuellt transaktions-ID.
Och vi mÄste förstÄ detta. Det Àr mycket viktigt, annars kan vi inte förstÄ lÄsning i Postgres.
Virtuellt transaktions-ID Àr ett transaktions-ID som inte innehÄller permanenta vÀrden. Till exempel, om jag kör ett SELECT-kommando, kommer jag troligen inte att Àndra databasen, jag kommer inte att blockera nÄgot. DÀrför, nÀr vi kör ett enkelt SELECT, tilldelar vi inte denna transaktion ett permanent ID. Vi ger den bara ett virtuellt ID.
Och detta ökar prestationen i Postgres, förbÀttrar möjligheterna för rensning, sÄ virtuellt transaktions-ID bestÄr av tvÄ nummer. Det första numret före snedstrecket Àr backend-ID:t. Och till höger ser vi bara en rÀknare.

SÄ nÀr jag kör en frÄga, sÀger det att backend-ID:t Àr 2.

Och om jag kör en serie sÄdana transaktioner ser vi att rÀknaren ökar varje gÄng jag kör en frÄga. Till exempel nÀr jag kör frÄga 2/10, 2/11, 2/12 och sÄ vidare.

Observera att det finns tvÄ kolumner hÀr. Till vÀnster ser vi det virtuella transaktions-ID:t - 2/12. Och till höger har vi det permanenta transaktions-ID:t. Och detta fÀlt Àr tomt. Denna transaktion modifierar inte databasen. DÀrför tilldelar jag den inte ett permanent transaktions-ID.

SÄ snart jag kör kommandot analysera (ANALYZE), ger samma frÄga mig ett permanent transaktions-ID. Se hur detta har förÀndrats. Tidigare hade jag inte detta ID, nu har jag det.

Och hÀr Àr Ànnu en frÄga, ytterligare en transaktion. Det virtuella transaktionsnumret - 2/13. Och om jag begÀr det permanenta transaktions-ID:t, fÄr jag det nÀr jag kör frÄgan.

SÄ, Ànnu en gÄng. Vi har det virtuella transaktions-ID:t och det permanenta transaktions-ID:t. FörstÄ detta för att förstÄ Postgres beteende.

Vi gÄr vidare till den tredje delen. HÀr kommer vi att gÄ igenom de olika typerna av lÄs i Postgres. Det Àr inte sÄ intressant. Den sista delen kommer att vara mycket mer spÀnnande. Men vi mÄste tÀcka de grundlÀggande sakerna, annars kommer vi inte att förstÄ vad som kommer hÀrnÀst.
Vi kommer att gÄ igenom denna del, vi kommer att titta pÄ varje typ av lÄs. Och jag kommer att visa er exempel pÄ hur de sÀtts, hur de fungerar, och ge er nÄgra frÄgor som ni kan anvÀnda för att se hur lÄs fungerar i Postgres.

För att skapa en frÄga och se vad som hÀnder i Postgres, behöver vi utföra en frÄga i systemvyn. I det hÀr fallet har vi markerat pg_lock med röd fÀrg. Pg_lock Àr en systemtabell som berÀttar för oss vilka lÄs som för nÀrvarande anvÀnds i Postgres.
Det Àr dock vÀldigt svÄrt för mig att visa er pg_lock som det Àr, eftersom det Àr ganska komplicerat. DÀrför har jag skapat en vy som visar pg_locks. Den utför ocksÄ lite arbete för mig som hjÀlper mig att förstÄ bÀttre. Det vill sÀga att den exkluderar mina lÄs, min egen session och sÄ vidare. Det Àr bara standard SQL och det gör att jag bÀttre kan visa er vad som hÀnder.

En annan utmaning Ă€r att denna vy Ă€r vĂ€ldigt bred, sĂ„ jag behöver skapa en andra vy â lockview2.
Den visar mig fler kolumner frÄn tabellen. Och en annan som visar mig de övriga kolumnerna. Det Àr ganska komplicerat, sÄ jag har försökt att presentera det sÄ enkelt som möjligt.

SÄ vi har skapat en tabell som heter Lockdemo. DÀr har vi skapat en rad. Det Àr vÄr provtabell. Och vi kommer att skapa avsnitt för att bara visa er exempel pÄ lÄs.

SÄ en rad, en kolumn. Den första typen av lÄs kallas ACCESS SHARE. Det Àr den minst begrÀnsande lÄset. Det betyder att den praktiskt taget inte konfliktar med andra lÄs.
Och om vi vill explicit definiera lÄsningen kör vi kommandot «lock table». Och det kommer tydligt att lÄsa, det vill sÀga i ACCESS SHARE-lÀge kör vi lock table. Och om jag kör PSQL i bakgrunden, sÄ startar jag pÄ det sÀttet en andra session frÄn min första session. Vad gör jag hÀr? Jag gÄr till en annan session och sÀger till den «visa mig lockview för denna förfrÄgan». Och hÀr har jag AccessShareLock i denna tabell. Det Àr precis vad jag begÀrde. Och den sÀger att lÄsningen har tilldelats. Mycket enkelt.

Vidare, om vi tittar pÄ den andra kolumnen, sÄ finns det inget dÀr. De Àr tomma.

Och om jag kör kommandot «SELECT», sÄ Àr det ett implicerat (explicit) sÀtt att begÀra AccessShareLock. SÄ jag slÀpper min tabell och kör förfrÄgan, och förfrÄgan returnerar flera rader. Och i en av raderna ser vi AccessShareLock. SÄledes anropar SELECT AccessShareLock i tabellen. Och den konflikerar praktiskt taget inte med nÄgot, eftersom det Àr en lÄsning av lÄg nivÄ.

Vad hÀnder om jag kör SELECT och har tre olika tabeller? Tidigare körde jag bara en tabell, nu kör jag tre: pg_class, pg_namespace och pg_attribute.

Och nu nÀr jag tittar pÄ förfrÄgan, ser jag 9 AccessShareLocks i tre tabeller. Varför? Tre tabeller markeras med blÄtt: pg_attribute, pg_class, pg_namespace. Men du kan ocksÄ se att alla index som definieras genom dessa tabeller ocksÄ har AccessShareLock.
Och det Àr en lÄsning som praktiskt taget inte konflikterar med andra. Och allt den gör Àr att helt enkelt förhindra oss frÄn att ÄterstÀlla tabellen sÄ lÀnge vi vÀljer den. Det har mening. Det vill sÀga, om vi vÀljer en tabell och den försvinner just dÄ, sÄ Àr det fel, sÄ AccessShare Àr en lÄsning av lÄg nivÄ som sÀger oss "ta inte bort denna tabell medan jag arbetar". I sjÀlva verket Àr det allt den gör.

ROW SHARE â det Ă€r en lĂ„sning som Ă€r lite annorlunda.

LÄt oss ta ett exempel. SELECT ROW SHARE Àr ett sÀtt att lÄsa varje rad för sig. SÄledes kan ingen ta bort dem eller Àndra dem medan vi ser pÄ dem.
SÄ vad gör SHARE LOCK? Vi ser att transaktions-ID 681 anvÀnds för SELECT. Och det Àr intressant. Vad har hÀnt hÀr? För första gÄngen ser vi ett nummer i 'Lock'-fÀltet. Vi tar transaktions-ID och det sÀger att det lÄser den i exklusivt lÀge. Allt det gör Àr att det sÀger att jag har en rad som tekniskt Àr lÄst nÄgonstans i tabellen. Men den sÀger inte var specifikt. Lite senare ska vi titta nÀrmare pÄ detta.

HÀr sÀger vi att lÄsning anvÀnds av oss.

SÄ, exklusiv lÄsning sÀger explicit att den Àr exklusiv. Och ocksÄ, om du tar bort en rad i denna tabell, sÄ kommer det att hÀnda, som du kan se.

SHARE EXCLUSIVE â det Ă€r en lĂ€ngre lĂ„sning.

Detta (ANALYZE) Àr en analyskommando som kommer att anvÀndas.

SHARE LOCK â du kan explicit lĂ„sa i delat lĂ€ge.

Du kan ocksÄ skapa en unik index. Och dÀr kan du se SHARE LOCK, som Àr en del av dem. och det lÄser tabellen och sÀtter ett SHARE LOCK pÄ den.
Som standard betyder SHARE LOCK pÄ tabellen att andra kan lÀsa tabellen, men ingen kan modifiera den. Och det Àr vad som hÀnder nÀr du skapar en unik index.
Om jag skapar en unik concurrent index, sÄ kommer jag att ha en annan typ av lÄsning, eftersom, som du kommer ihÄg, att anvÀnda concurrent index minskar lÄskravet. Och om jag anvÀnder normal lÄsning, normal index, sÄ hindrar jag pÄ sÄ sÀtt skrivandet i indexet under skapandet. Om jag anvÀnder concurrent index, sÄ mÄste jag anvÀnda en annan typ av lÄsning.

SHARE ROW EXCLUSIVE â Ă€n en gĂ„ng kan den anges explicit.

Eller sÄ kan vi skapa en regel, dvs. ta ett visst fall dÀr den kommer att anvÀndas.

EXCLUSIVE lÄsning betyder att ingen annan kan Àndra tabellen.

HÀr ser vi olika typer av lÄsningar.

ACCESS EXCLUSIVE, till exempel, Àr en lÄsningskommando. Till exempel, om du gör CLUSTER table, sÄ kommer det att betyda att ingen kan skriva dit. Och den lÄser inte bara sjÀlva tabellen, utan ocksÄ indexen.

Detta Àr den andra sidan av ACCESS EXCLUSIVE-lÄsning, dÀr vi ser specifikt vad den lÄser i tabellen. Den lÄser individuella rader i tabellen, vilket Àr ganska intressant.
Det hÀr Àr all grundlÀggande information som jag ville ge. Vi har pratat om lÄs, transaktions-ID, virtuella transaktions-ID och permanenta transaktions-ID.

Och nu ska vi gÄ igenom exempel pÄ lÄs. Detta Àr den mest intressanta delen. Vi kommer att titta pÄ vÀldigt intressanta fall. Min uppgift i den hÀr presentationen Àr att ge er en bÀttre förstÄelse för vad Postgres faktiskt gör nÀr det försöker lÄsa olika saker. Jag tycker att det Àr mycket bra pÄ att lÄsa specifika delar.
LÄt oss titta pÄ vissa exempel.

Vi börjar med tabeller och en rad i tabellen. NÀr jag sÀtter in nÄgot visas ExclusiveLock, transaktions-ID och ExclusiveLock pÄ tabellen.

Vad hÀnder om jag sÀtter in ytterligare tvÄ rader? Och nu har vi tre rader i vÄr tabell. Jag satte in en rad och fick detta som utdata. Och om jag sÀtter in ytterligare tvÄ rader, vad Àr konstigt hÀr? HÀr finns det en konstighet, för jag har lagt till tre rader till denna tabell, men jag har fortfarande tvÄ rader i lÄstabellen. Och det Àr i grunden Postgres grundlÀggande beteende.
MÄnga tror att om du blockerar 100 rader i databasen, sÄ kommer du behöva skapa 100 lÄspostningar. Om jag blockerar 1 000 rader pÄ en gÄng, sÄ behöver jag 1 000 sÄdana förfrÄgningar. Och om jag behöver blockera en miljon eller en miljard. Men om vi gör sÄ, kommer det inte fungera sÀrskilt bra. Om du har anvÀnt ett system som skapar lÄsposter för varje enskild rad sÄ ser du att det Àr komplicerat. För du mÄste definiera lÄstabellen med en gÄng, och den kan bli överfull. Men Postgres gör inte sÄ.
Och pÄ det hÀr bilden Àr det mycket viktigt att det tydligt visas att det finns ytterligare ett system som arbetar inom MVCC, som lÄser enskilda rader. SÄ nÀr du blockerar miljarder rader, skapar Postgres inte en miljard separata lÄsningar. Och det pÄverkar prestandan mycket positivt.

Hur Àr det med uppdateringen? Jag uppdaterar just nu en rad, och du kanske mÀrker att den omedelbart utförde tvÄ olika operationer. Den blockerade tabellen, men den blockerade ocksÄ indexet. Det var nödvÀndigt att blockera indexet eftersom det finns unika begrÀnsningar pÄ denna tabell. Vi vill sÀkerstÀlla att ingen Àndrar det, sÄ vi blockerar det.

Vad hÀnder om jag vill uppdatera tvÄ rader? Och vi ser att den beter sig likadant. Vi genomför dubbelt sÄ mÄnga uppdateringar, men exakt samma antal radblockeringar.
Om du undrar hur Postgres gör detta, behöver du lyssna pÄ mina presentationer om MVCC för att förstÄ hur Postgres internt mÀrker dessa rader som den Àndrar. Och Postgres har ett sÀtt att göra detta pÄ, men det görs inte pÄ tabellblockeringsnivÄ; det görs pÄ en lÀgre och mer effektiv nivÄ.

Men vad hÀnder om jag vill ta bort nÄgot? Om jag tar bort till exempel en rad och jag fortfarande har mina tvÄ lÄs kvar, Àven om jag skulle vilja ta bort dem allihop, kommer de fortfarande att finnas dÀr.

Och till exempel, om jag vill infoga 1 000 rader och sedan antingen ta bort eller lÀgga till 1 000 rader, sÄ skrivs de individuella raderna som jag lÀgger till eller Àndrar inte hÀr. De skrivs till en lÀgre nivÄ inne i sjÀlva raden. Under presentationen om MVCC pratade jag om detta i detalj. Men det Àr mycket viktigt, nÀr du analyserar blockeringar, att sÀkerstÀlla att du har blockering pÄ tabellnivÄ och att du hÀr inte ser hur individuella rader skrivs.

Vad sÀgs om explicit blockering?

Om jag trycker pÄ 'uppdatera', sÄ har jag tvÄ lÄsta rader. Och om jag vÀljer alla och trycker pÄ 'uppdatera överallt', sÄ har jag fortfarande tvÄ blockinspelningar.

Vi skapar inte separata poster för varje enskild rad. För annars sjunker prestandan, och det kan bli för mycket. Vi kan hamna i en obehaglig situation.

Och samma sak gÀller om vi gör pÄ delad nivÄ, vi kan göra det 30 gÄnger.

Vi ÄterstÀller vÄr tabell, raderar allt, och sedan lÀgger vi igen till en rad.

En annan typ av beteende som du ser i Postgres Ă€r det mycket vĂ€lkĂ€nda och önskvĂ€rda beteendet â det Ă€r att du kan utföra en update eller select. Och du kan göra detta samtidigt. Och select blockerar inte update och vice versa. Vi sĂ€ger till lĂ€saren att inte blockera den som skriver, och den som skriver blockerar inte lĂ€saren.
Jag ska visa dig ett exempel pĂ„ detta. Jag kommer nu att göra ett val. Sedan kommer vi att göra en INSERT. Och du kommer att kunna se â 694. Du kommer att se ID för transaktionen som gjorde denna insĂ€ttning. Och sĂ„ hĂ€r fungerar det.

Och om jag nu tittar pĂ„ min backend-ID, sĂ„ har den blivit â 695.

Och jag kan se att 695 visas i min tabell.

Och om jag gör en uppdatering hÀr sÄ hÀr, fÄr jag ett annat fall. I detta fall Àr 695 en exklusiv lÄsning, och uppdateringen har samma beteende, men mellan dem uppstÄr ingen konflikt, vilket Àr ganska ovanligt.
Och du kan mÀrka att överst Àr det ShareLock, och lÀngst ner Àr det ExclusiveLock. Och bÄda transaktionerna blev framgÄngsrika.
Du bör lyssna pÄ mitt föredrag om MVCC för att förstÄ hur detta sker. Men detta Àr en illustration av att du kan göra detta samtidigt, dvs. göra SELECT och UPDATE pÄ en gÄng.

LÄt oss ÄterstÀlla och göra en operation igen.

Om du försöker köra tvÄ update samtidigt pÄ samma rad, kommer det att blockeras. Och kom ihÄg att jag sa att en lÀsare inte blockerar en skrivare, och en skrivare blockerar inte en lÀsare, men en skrivare blockerar en annan skrivare. Det vill sÀga, vi kan inte lÄta tvÄ personer samtidigt uppdatera samma rad. De mÄste vÀnta tills en av dem Àr klar.

Och för att illustrera detta kommer jag att titta pÄ Lockdemo-tabellen. Och vi ska titta pÄ en rad. PÄ transaktion 698.
Vi uppdaterade detta till 2. 699 â det första uppdateringen. Och det har gĂ„tt igenom framgĂ„ngsrikt eller sĂ„ Ă€r det i en vĂ€ntande transaktion och vĂ€ntar pĂ„ att vi ska bekrĂ€fta eller avbryta.

Men titta pĂ„ det hĂ€r â 2/51 â det Ă€r vĂ„r första transaktion, vĂ„r första session. 3/112 â det Ă€r den andra förfrĂ„gan som dök upp ovanifrĂ„n och som Ă€ndrade detta vĂ€rde till 3. Och om du lĂ€gger mĂ€rke till, sĂ„ blockerade den övre sig sjĂ€lv, vilken var 699. Men 3/112 gav ingen blockering. I kolumnen Lock_mode stĂ„r det att den vĂ€ntar. Den vĂ€ntar pĂ„ 699. Och om du kollar var 699 Ă€r, sĂ„ Ă€r den högre upp. Och vad gjorde den första sessionen? Den skapade en exklusiv blockering pĂ„ sitt eget transaktions-ID. Det Ă€r sĂ„ Postgres gör. Den blockerar sitt eget transaktions-ID. Och om du vill vĂ€nta pĂ„ att nĂ„gon ska bekrĂ€fta eller avbryta, sĂ„ mĂ„ste du vĂ€nta pĂ„ att det finns en vĂ€ntande transaktion. Och dĂ€rför kan vi se en konstig rad.
LÄt oss titta igen. Till vÀnster ser vi vÄrt processerings-ID. I den andra kolumnen ser vi vÄrt virtuella transaktions-ID, och i den tredje ser vi lock_type. Vad betyder det? I princip sÀger den att den blockerar transaktions-ID. Men lÀgg mÀrke till att det i alla rader lÀngst ner stÄr relation. Och dÀrför har du tvÄ typer av blockeringar i tabellen. Det finns en relationblockering. Och det finns ocksÄ en transactionid-blockering, dÀr vi blockerar sjÀlva, vilket exakt Àr det som hÀnder i den första raden eller lÀngst ner, dÀr transactionid, dÀr vi vÀntar pÄ att 699 ska avsluta sin operation.
Jag ser vad som hÀnder hÀr. Och hÀr sker tvÄ saker samtidigt. Du tittar pÄ blockeringen av transaktions-ID i den första raden, som blockerar sig sjÀlv. Och den blockerar sig sjÀlv för att fÄ folk att vÀnta.
Om du tittar pÄ den sjÀtte raden, sÄ Àr det samma post som den första. Och dÀrför blockeras transaktionen 699. 700 blockerar ocksÄ sig sjÀlv. Och sedan i den nedersta raden kommer du se att vi vÀntar pÄ att 699 ska avsluta sin operation.

Och i lock_type, tuple ser du siffrorna.

Du kan se att det Àr 0/10. Och det Àr sidnumret, och Àven offset för denna specifika rad.

Och du ser att det blir 0/11 nÀr vi uppdaterar.

Men egentligen Àr det 0/10, eftersom denna operation vÀntar. Vi har möjlighet att se att det Àr den rad som jag vÀntar pÄ att bekrÀfta.

SÄ snart vi bekrÀftade det och tryckte pÄ commit, och nÀr uppdateringen var klar, Àr det hÀr vad vi fÄr igen. Transaktion 700 Àr den enda lÄsningen, den vÀntar inte lÀngre pÄ nÄgon annan eftersom den har blivit commitad. Den vÀntar bara pÄ att transaktionen ska avslutas. SÄ snart 699 Àr klar vÀntar vi inte lÀngre pÄ nÄgot. Och nu sÀger transaktion 700 att allt Àr okej, att den har alla lÄs som den behöver i alla tillÄtna tabeller.

Och för att komplicera allt detta ytterligare skapar vi en till vy, som den hÀr gÄngen kommer att ge oss en hierarki. Jag förvÀntar mig inte att ni förstÄr denna frÄga. Men det kommer att ge oss en tydligare bild av vad som hÀnder.

Det Àr en rekursiv vy, som ocksÄ har ett annat avsnitt. Och det returnerar sedan allt ihop. LÄt oss anvÀnda det.

Vad hÀnder om vi gör tre samtidiga uppdateringar och sÀger att raden just nu Àr tre. Och vi förÀndrar 3 till 4.

Och hÀr ser vi 4. Och transaktions-ID 702.

Och sedan byter jag 4 till 5. Och 5 till 6, och 6 till 7. Och jag köar en rad personer som kommer att vÀnta pÄ att denna enskilda transaktion ska avslutas.

Och allt blir tydligt. Vilken Àr den första raden? Det Àr 702. Det Àr transaktions-ID som ursprungligen satte detta vÀrde. Och vad har jag skrivit i kolumnen Granted? Jag har markeringar f. Det Àr mina uppdateringar, som (5, 6, 7), som inte kan godkÀnnas eftersom vi vÀntar pÄ att transaktions-ID 702 ska avslutas. DÀr har vi en lÄsning av transaktions-ID. Och det ger 5 transaktionslÄsning ID.
Och om ni tittar pÄ 704, pÄ 705, sÄ stÄr det fortfarande ingenting dÀr, eftersom de fortfarande inte vet vad som hÀnder. De skriver bara att de inte har en aning om vad som hÀnder. Och de kommer helt enkelt att somna, eftersom de vÀntar pÄ att nÄgon ska avsluta och vÀcka dem nÀr det blir möjligt att Àndra raden.

SÄ hÀr ser det ut. Det Àr tydligt att de alla vÀntar pÄ rad 12.

Detta Àr vad vi sÄg hÀr. HÀr Àr 0/12.

SÄ snart den första transaktionen Àr godkÀnd, kan ni hÀr se hur hierarkin fungerar. Och nu blir allt klart. De alla blir rena. Och de Àr faktiskt fortfarande i vÀntelÀge.

Det hÀr Àr vad som hÀnder. 702 gör en commit. Och nu fÄr 703 denna radlÄsningsstatus, och sedan börjar 704 vÀnta pÄ att 703 ska göra en commit. Och 705 vÀntar ocksÄ pÄ detta. NÀr allt detta avslutas stÀdar de upp efter sig sjÀlva. Jag vill pÄpeka att alla stÀller sig i kö. Det liknar verkligen en trafikstockning, dÀr alla vÀntar pÄ att den första bilen ska passera. Den första bilen stannar, och alla stÀller sig i en lÄng rad. Sedan rör den sig, sedan kan nÀsta bil köra fram och fÄ sin lÄsning och sÄ vidare.

Och om du tyckte att det hÀr var tillrÀckligt komplicerat, sÄ ska vi nu prata om deadlocks. Jag vet inte vem av er som har stött pÄ dem. Det hÀr Àr ett ganska vanligt problem i databassystem. Men deadlocks Àr det fall dÄ en session vÀntar pÄ att nÄgot ska utföras av en annan session. Samtidigt vÀntar den andra sessionen pÄ att den första sessionen ska utföra nÄgot.
Till exempel, om Ivan sÀger: "Ge mig nÄgot", och jag sÀger: "Nej, jag ger dig det bara om du ger mig nÄgot annat". Och han sÀger: "Nej, jag ger dig inte det om du inte ger mig". Och vi hamnar i en deadlock-situation. Jag Àr sÀker pÄ att Ivan inte skulle göra sÄ, men du förstÄr poÀngen, vi har tvÄ personer som vill ha nÄgot och de Àr inte villiga att ge det förrÀn den andra personen ger dem det de vill ha. HÀr finns det ingen lösning.
I grund och botten behöver din databas upptÀcka detta. Och du mÄste sedan avsluta eller stÀnga en av sessionerna, för annars kommer de att stanna dÀr för alltid. Vi ser detta i databaser, vi ser det i operativsystem. Och överallt dÀr vi har parallella processer kan detta hÀnda.

Nu kommer vi att sÀtta upp tvÄ deadlocks. Vi sÀtter 50 och 80. I den första raden kommer jag att göra en uppdatering frÄn 50 till 50. Jag fÄr transaktionsnummer 710.

Sedan kommer jag att Àndra 80 till 81, och 50 till 51.

Och sÄ hÀr kommer det att se ut. DÀrför har 710 en radlÄsning, medan 711 vÀntar pÄ bekrÀftelse. Vi sÄg detta nÀr vi uppdaterade. 710 Àr Àgaren av vÄr rad. Och 711 vÀntar pÄ att 710 ska avsluta transaktionen.

Och det stÄr till och med pÄ vilken specifik rad vi har en deadlock. Och hÀr börjar det bli konstigt.

Nu uppdaterar vi 80 till 80.

Och hÀr Àr dÀr deadlocks börjar. 710 vÀntar pÄ svar frÄn 711, medan 711 vÀntar pÄ 710. Och det kommer att sluta illa. Det finns ingen utvÀg hÀr. Och de kommer att vÀnta pÄ varandras svar.

Och det hÀr kommer bara att börja försena allt. Och vi vill inte ha det.

Och i Postgres finns det sÀtt att mÀrka nÀr detta hÀnder. Och nÀr det hÀnder fÄr du ett sÄdant felmeddelande. Och det blir tydligt att en viss process vÀntar pÄ SHARE LOCK frÄn en annan process, det vill sÀga en som blockeras av process 711. Och den processen vÀntade pÄ att fÄ SHARE LOCK pÄ en viss transaktions-ID och blev blockerad av en viss process. SÄ situationen Àr en deadlock.

Men finns det trevĂ€gs-deadlocks? Ăr det möjligt? Ja.

Vi matar in dessa siffror i tabellen. Vi Àndrar 40 till 40, vi skapar en lÄsning.

Vi Àndrar 60 till 61, 80 till 81.

Och sedan Ă€ndrar vi 80, och sedan â boom!

Och 714 vÀntar nu pÄ 715. 716 vÀntar pÄ 715. Och med det gÄr det inte att göra nÄgot.

HÀr Àr det inte lÀngre tvÄ personer, hÀr Àr det redan tre personer. Jag vill ha nÄgot frÄn dig, den hÀr vill ha nÄgot frÄn den tredje personen, och den tredje personen vill ha nÄgot frÄn mig. Och vi hamnar i en trevÀgsvÀntan, eftersom vi alla vÀntar pÄ att den andra personen ska avsluta vad de mÄste göra.

Och Postgres vet pÄ vilken rad detta hÀnder. Och dÀrför ger det dig följande meddelande, som visar att du har ett problem dÀr tre ingÄngar blockerar varandra. Och det finns inga begrÀnsningar. Det kan vara i ett fall dÀr 20 poster blockerar varandra.

NÀsta problem Àr serializable.

Om det Àr en speciell serializable-lÄsning.

Och vi gÄr tillbaka till 719. Den har en fullt normal utdata.

Och du kan klicka för att göra en transaktion frÄn serializable.

Och du förstĂ„r att du nu har en annan typ av lĂ„sning SA â det betyder serializable.


Och dÀrför har vi en ny typ av lÄsning som kallas SARieadLock, som Àr en seriell lÄsning och tillÄter inmatning av serienummer.

Och du kan ocksÄ infoga unika index.

I denna tabell har vi unika index.

SÄ om jag matar in siffran 2 hÀr, sÄ fÄr jag 2. Men högst upp matar jag in Ànnu en 2. Och du kan se att 721 har en exklusiv lÄsning. Men nu vÀntar 722 pÄ att 721 ska avsluta sin operation, eftersom den inte kan infoga 2 förrÀn den vet vad som kommer att hÀnda med 721.

Och om vi gör en subtransaction.

HĂ€r har vi 723.

Och om vi sparar punkten och sedan uppdaterar den, fÄr vi en ny transaktions-ID. Detta Àr ytterligare ett beteende som du behöver kÀnna till. Om vi Äterför detta, avlÀgsnas transaktions-ID:n. 724 försvinner. Men nu fÄr vi 725.
Och vad försöker jag göra hĂ€r? Jag försöker visa dig exempel pĂ„ ovanliga lĂ„s som du kan stöta pĂ„: oavsett om det handlar om serializable-lĂ„s eller SAVEPOINT â det Ă€r olika typer av lĂ„s som kommer att dyka upp i lĂ„s tabellen.

Detta handlar om att skapa explicita (eller explicita) lÄs, som har pg_advisory_lock.

Och du ser att lÄstypen hÀr anges som advisory. Och hÀr stÄr det skrivet i rött "advisory". Och du kan samtidigt lÄsa sÄ hÀr med pg_advisory_unlock.

Och för att avsluta vill jag visa dig en annan spÀnnande sak. Jag kommer att skapa en annan typ. Men jag kommer att sammanfoga tabellen pg_locks med tabellen pg_stat_activity. Och varför vill jag göra detta? För att detta gör att jag kan se och fÄ en översikt av alla aktuella sessioner och se vilka specifika lÄs de vÀntar pÄ. Och detta Àr ganska intressant nÀr vi sÀtter samman lÄstabellen och frÄgetabellen.

Och hÀr skapar vi pg_stat_view.

Och vi uppdaterar raden med ett. Och hĂ€r ser vi 724. Sedan uppdaterar vi vĂ„r rad till tre. Och vad ser du hĂ€r nu? Det Ă€r förfrĂ„gningar, dvs. du ser hela listan över förfrĂ„gningar som listas i vĂ€nster kolumn. Och sedan kan du se lĂ„sen och vad de skapar pĂ„ högra sidan. Och detta kan vara mer begripligt för dig, sĂ„ du inte behöver gĂ„ tillbaka till varje session varje gĂ„ng och kolla â om du behöver gĂ„ med i den eller inte. Det görs för oss.
En annan funktion som Àr mycket anvÀndbar Àr pg_blocking_pidsDu har kanske aldrig hört talas om det. Vad gör det? Det lÄter oss sÀga att för den hÀr sessionen 11740, vilka specifika process-ID:n den förvÀntar sig. Och du kan se att 11740 förvÀntar sig 724. Och 724 Àr högst upp. Och 11306 Àr ditt process-ID. I princip gÄr den hÀr funktionen igenom din lÄsningstabell. Och jag vet att det Àr lite komplicerat, men du verkar förstÄ det. I princip gÄr den hÀr funktionen igenom den hÀr lÄsningstabellen och försöker hitta var detta proces-ID finns, med hÀnsyn till de lÄsningar den vÀntar pÄ. Den försöker ocksÄ rÀkna ut vilket specifikt proces-ID som den process har som vÀntar pÄ lÄsningar. SÄ du kan köra den hÀr funktionen. pg_blocking_pids.
Och det kan vara vÀldigt anvÀndbart. Vi lade till detta först i version 9.6, sÄ den hÀr funktionen Àr bara 5 Är gammal, men den Àr mycket och verkligen anvÀndbar. Detsamma gÀller den andra frÄgan. Den visar exakt det vi behöver se.

Det Àr det jag ville prata med er om. Och som jag förvÀntade mig anvÀnde vi all vÄr tid, eftersom det var sÄ mÄnga bilder. Och bilderna finns tillgÀngliga för nedladdning. Jag vill tacka er för att ni kom hit. Jag Àr sÀker pÄ att ni kommer att uppskatta resten av konferensen, stort tack!
FrÄgor:
Till exempel, om jag försöker uppdatera rader, medan en annan session försöker ta bort hela tabellen. SÄvitt jag förstÄr skulle det behöva finnas nÄgot som heter intent lock. Finns det nÄgot sÄdant i Postgres?

Vi gÄr tillbaka till början. Kanske minns du att nÀr du gör vad som helst, till exempel nÀr du gör en SELECT, sÄ ger vi AccessShareLock. Och detta förhindrar att tabellen tas bort. SÄ om du till exempel vill uppdatera en rad i tabellen eller ta bort en rad, sÄ kan ingen ta bort hela tabellen samtidigt, eftersom du hÄller det hÀr AccessShareLock över hela tabellen och över raden. Och nÀr du Àr klar kan de ta bort den. Men sÄ lÀnge du gör nÄgot dÀr, kan de inte göra det.
LÄt oss gÄ igenom det en gÄng till. LÄt oss ta exemplet med borttagning. Och du ser hur det finns en exklusiv lÄs över hela tabellen pÄ raden.
Det skulle se ut som en exklusiv lÄs, eller hur?
Ja, det verkar sÄ. Jag förstÄr vad du menar. Du sÀger att om jag utför SELECT kommer jag att ha ShareExclusive, och om jag sedan Àndrar detta till Row Exclusive, blir det ett problem? Men överraskande nog skapar detta inget problem. Det verkar som om jag ökar lÄsningsgraden, men i grunden har jag en lÄs som förhindrar borttagning. Och just nu, nÀr jag gör denna lÄs starkare, förhindrar den fortfarande borttagning. SÄ det handlar inte om att jag höjer mig. Det vill sÀga, den förhindrade detta Àven nÀr den var pÄ en lÀgre nivÄ, sÄ nÀr jag höjer dess nivÄ förhindrar den fortfarande borttagning av tabellen.
Jag förstÄr vad du menar. HÀr finns inget fall av ökning av lÄsningsgraden dÀr du försöker frigöra en lÄs för att införa en starkare. HÀr ökar det bara generellt denna förhindran, sÄ det orsakar ingen konflikt. Men det Àr en bra frÄga. Tack sÄ mycket för att du stÀllde den!
Vad behöver vi göra för att undvika situationen med deadlock nÀr vi har mÄnga sessioner och ett stort antal anvÀndare?
Postgres upptÀcker automatiskt situationer med deadlock. Och det kommer automatiskt att ta bort en av sessionerna. Det enda sÀttet att undvika dödlÄsningssituationer Àr att lÄsa personer i samma ordning. SÄ nÀr du tittar pÄ din applikation Àr anledningen till deadlocks ofta... LÄt oss sÀga att jag vill lÄsa tvÄ olika saker. En applikation lÄser tabell 1, och en annan applikation lÄser 2, och sedan tabell 1. Det enklaste sÀttet att undvika deadlocks Àr att titta pÄ din applikation och försöka se till att lÄsningen sker i samma ordning i alla applikationer. Och det brukar ta bort 80 % av problemen, eftersom olika mÀnniskor skriver dessa applikationer. Och om du lÄser dem i samma ordning, sÄ stöter du inte pÄ dödlÄsningssituationen.
Tack sÄ mycket för din presentation! Du talade om vacuum full och om jag förstÄr rÀtt, sÄ deformerar vacuum full ordningen pÄ poster i en sÀrskild lagring, sÄ nuvarande poster förblir oförÀndrade. Men varför tar vacuum full exklusiv lÄsning och varför stÄr det i konflikt med skrivoperationer?
Det Àr en bra frÄga. Anledningen Àr att vacuum full tar tabellen. Och vi skapar i sjÀlva verket en ny version av tabellen. Tabellen kommer att bli ny. Det innebÀr att det blir en helt ny version av tabellen. Problemet Àr att nÀr vi gör detta, vill vi inte att folk ska lÀsa den, för vi behöver att de ser den nya tabellen. DÀrför kopplar det samman med den tidigare frÄgan. Om vi kunde lÀsa samtidigt, skulle vi inte kunna flytta den och rikta folk till den nya tabellen. Vi skulle behöva vÀnta tills alla hade slutat lÀsa denna tabell, och dÀrför handlar det i grunden om en exklusiv lÄsning.
Vi sÀger helt enkelt att vi lÄser frÄn början, eftersom vi vet att vi i slutet kommer att behöva en exklusiv lÄsning för att flytta alla till den nya kopian. SÄ potentiellt kan vi lösa det. Och vi gör det med samtidig indexering. Men det Àr mycket svÄrare att göra. Och det berör starkt din tidigare frÄga om exklusiv lÄsning.
Ăr det möjligt att lĂ€gga till locking timeout i Postgres? I Oracle kan jag till exempel skriva "vĂ€lj för uppdatering" och vĂ€nta 50 sekunder innan uppdateringen. Det fungerade bra för applikationen. Men i Postgres mĂ„ste jag antingen göra det omedelbart och inte vĂ€nta alls, eller vĂ€nta till en viss tid.
Ja, du kan vÀlja timeout för dina lÄs. Du kan ocksÄ ge kommandot no way, som kommer att ⊠om du inte kan fÄ lÄs omedelbart. SÄ antingen lock timeout eller nÄgot annat som tillÄter dig att göra det. Det görs inte pÄ syntaktisk nivÄ. Det görs som en variabel pÄ servern. Ibland gÄr det inte att anvÀnda.
Kan du öppna 75-sidan?
Ja.

Och min frÄga Àr följande. Varför vÀntar bÄda uppdateringsprocesserna pÄ 703?
Och det Àr en utmÀrkt frÄga. Jag förstÄr inte, förresten, varför Postgres gör sÄ hÀr. Men nÀr 703 skapades, förvÀntade den sig 702. Och nÀr 704 och 705 dyker upp verkar det som att de inte vet vad de vÀntar pÄ, eftersom det fortfarande inte finns nÄgot dÀr. Och Postgres gör sÄ hÀr: nÀr du inte kan fÄ en lÄs, sÄ skriver den "Vad Àr meningen med att behandla er?" eftersom du redan vÀntar pÄ nÄgon. SÄ vi lÄter bara lÄsningen hÀnga i luften, den uppdaterar inte detta över huvud taget. Men vad hÀnde hÀr? SÄ snart 702 fullföljde processen och 703 fick sin lÄs, sÄ Ätergick systemet. Och sade att nu har vi tvÄ personer som vÀntar. Och sedan uppdaterar vi dem tillsammans. Och vi anger att bÄda vÀntar.
Jag vet inte varför Postgres gör sĂ„ hĂ€r. Men det finns ett problem som kallas fâŠ. Jag tror att det inte Ă€r en term pĂ„ svenska. Det Ă€r nĂ€r alla vĂ€ntar pĂ„ en lĂ„s, Ă€ven om det finns 20 instanser som vĂ€ntar pĂ„ lĂ„set. Och plötsligt vaknar de alla samtidigt. Och alla börjar försöka reagera. Men systemet gör sĂ„ att alla vĂ€ntar pĂ„ 703. Eftersom de alla vĂ€ntar, och vi placerar dem omedelbart i kö. Och om nĂ„gon annan ny förfrĂ„gan dyker upp som skapades efter detta, till exempel 707, sĂ„ kommer det att bli tomt igen.
Och jag tror att detta görs för att man ska kunna sÀga att vid denna tidpunkt vÀntar 702 pÄ 703, och alla som kommer efter detta kommer inte ha nÄgon post i detta fÀlt. Men sÄ snart den första som vÀntar lÀmnar, och alla som vÀntade vid den tidpunkten innan uppdateringen fÄr samma mÀrke. Och dÀrför tror jag att det hÀr görs för att vi ska kunna bearbeta dem i ordning, sÄ att de Àr korrekt sorterade.
Jag har alltid sett detta som ett ganska konstigt fenomen. Eftersom vi hÀr till exempel inte listar dem överhuvudtaget. Men jag tror att varje gÄng vi ger en ny lÄs, sÄ ser vi pÄ alla som Àr i vÀntande process. DÄ stÀller vi dem alla i kö. Och sedan kommer vilken ny som helst som dyker upp, in i kön först nÀr nÀsta person har avslutat behandlingen. Mycket bra frÄga. Tack sÄ mycket för frÄgan!
Jag tycker att det Àr mycket mer logiskt nÀr 705 vÀntar pÄ 704.
Men problemet hÀr Àr det följande. Tekniskt sett kan ni vÀcka antingen den ena eller den andra. Och dÀrför kommer vi att vÀcka den ena eller den andra. Men vad hÀnder i systemets arbete? Ni ser hur 703 i toppen blockerade sitt eget transaktions-ID. SÄ fungerar Postgres. Och 703 blockeras av sitt eget transaktions-ID, sÄ om nÄgon vill vÀnta, kommer de att vÀnta pÄ 703. Och i grund och botten avslutar 703. Och först efter att den har avslutats vÀcks nÄgon av processerna. Och vi vet inte vilken process som kommer att vÀckas först. Sedan hanterar vi allt gradvis. Men det Àr oklart vilken process som vÀcks först, eftersom det kan vara nÄgon av dessa processer. I grund och botten hade vi en schemalÀggare som sa att vi nu kan vÀcka vilken av dessa processer som helst. Vi vÀljer helt enkelt en slumpmÀssigt. DÀrför mÄste bÄda noteras, eftersom vi kan vÀcka nÄgon av dem.
Och problemet Àr att vi har CP-oÀndlighet. Och dÀrför Àr det helt möjligt att vi kan vÀcka den senare. Och om vi till exempel vÀcker den senare, kommer vi att vÀnta pÄ den som just fick en blockering, sÄ vi kan inte avgöra vem som kommer att vÀckas först. Vi skapar helt enkelt en sÄdan situation, och systemet kommer att vÀcka dem i godtycklig ordning.
Det finns . Titta, de Àr ocksÄ intressanta och anvÀndbara. Temat Àr sjÀlvklart fruktansvÀrt komplext. Tack sÄ mycket, Bruce!
KĂ€lla: habr.com
