2020. gada Bruisa Momziāna ziņojuma "Atbloķējot Postgres Lock Manager" atšifrējums.

(Piezīme: Visus SQL vaicājumus no slaidiem varat saņemt pa šo saiti: )
Sveiki! Ir lieliski atkal būt šeit Krievijā. Atvainojos, ka pagājušajā gadā nevarēju ierasties, bet šogad Ivanam un man ir lieli plāni. Ceru, ka būšu šeit daudz biežāk. Man ļoti patīk ierasties Krievijā. Plānoju apmeklēt Tjumeņu un Tveru. Esmu ļoti priecīgs, ka man būs iespēja apmeklēt šos pilsētas.
Mani sauc Bruiss Momziāns. Es strādāju uzņēmumā EnterpriseDB un ar Postgres strādāju vairāk nekā 23 gadi. Es dzīvoju Filadelfijā, ASV. Ceļoju aptuveni 90 dienas gadā un apmeklēju aptuveni 40 konferencēs. Mans , kurā ir slaidi, kurus tagad jums rādīšu. Tādēļ pēc konferences jūs varat tos lejupielādēt no manas personīgās vietnes. Tur ir arī aptuveni 30 prezentācijas, kā arī video un liela daļa emuāru ierakstu, vairāk nekā 500. Tas ir ļoti ietilpīgs resurss. Ja jums interesē šis materiāls, es aicinu jums to izmantot.
Agrāk biju pasniedzējs, profesors, pirms sāku strādāt ar Postgres. Un esmu ļoti priecīgs, ka šobrīd varu jums pastāstīt to, ko plānoju. Tā ir viena no manām interesantākajām prezentācijām. Un šī prezentācija satur 110 slaidus. Mēs sāksim ar vienkāršām lietām, un līdz prezentācijas beigām tas kļūs arvien sarežģītāk.

Tas ir diezgan nepatīkams temats. Bloķēšana nav populārākā tēma. Mēs vēlamies, lai tā pazustu. Tas ir kā doties pie zobārsta.

- Bloķēšana ir problēma daudzām cilvēkiem, kuri strādā ar datu bāzēm un kuriem strādā vienlaicīgi vairāki procesi. Viņiem ir nepieciešama bloķēšana. T.i., šodien es jums sniegšu pamatzināšanas par bloķēšanu.
- Transakciju identifikatori. Tā ir diezgan garlaicīga prezentācijas daļa, bet to ir nepieciešams saprast.
- Tālāk runāsim par bloķēšanas veidiem. Tā ir diezgan mehāniska daļa.
- Un tālāk mēs sniegsim dažus piemērus par bloķēšanu. Tas būs diezgan grūti uztverams.

Runāsim par bloķēšanām.

Terminoloģija mums ir diezgan sarežģīta. Cik no jums zina, no kurienes šis fragments? Divi cilvēki. Tas ir no spēles, kas saucās „Kolossāla piedzīvojumu ala”. Tas bija teksta datoru spēle astoņdesmitajos gados, man liekas. Tur bija jāieiet alā, labirintā, un teksts mainījās, bet saturs katru reizi bija aptuveni vienāds. Tā es atceros šo spēli.

Un šeit mēs redzam bloķēšanas nosaukumus, kas nāk no Oracle. Mēs tos izmantojam.

Šeit mēs redzam terminus, kas mani apjauc. Piemēram, SHARE UPDATE EXCLUSIVE. Tālāk SHARE RAW EXCLUSIVE. Godīgi sakot, šie nosaukumi nav ļoti saprotami. Mēs mēģināsim tos detalizētāk izskatīt. Dažos ir vārds „share”, kas nozīmē – norobežoties. Dažos ir vārds „exclusive” — ekskluzīvs. Dažos ir abi šie vārdi. Es gribētu sākt ar to, kā šie bloķējumi darbojas.

Un arī ļoti svarīgs ir vārds „piekļuve” — access. Un vārds „row” — rinda. T. i., piekļuves sadalījums, rindu sadalījums.

Vēl viena problēma, kuru nepieciešams saprast Postgres, es diemžēl nevarēšu stāstīt par to savā uzrunā, ir MVCC. Man ir atsevišķa prezentācija par šo tēmu manā mājaslapā. Un ja jūs domājat, ka šī prezentācija ir sarežģīta, tad MVCC – tas, iespējams, ir mans sarežģītākais temats. Un ja jūs interesē, jūs varat to apskatīt mājaslapā. Varat noskatīties video.

Vēl viens jautājums, ko mums nepieciešams saprast – ir transakciju identifikatori. Daudzas transakcijas nevar darboties bez unikāliem identifikatoriem. Un te ir skaidrojums par to, kas ir transakcija. Postgres ir divas transakciju nummurēšanas sistēmas. Es zinu, ka tas nav ļoti skaists risinājums.

Arī ņemiet vērā, ka slaidi būs diezgan sarežģīti uztverei, tāpēc uz to, kas izcelts sarkanā krāsā, tieši uz to jāuzmanās.

Skatāmies. Sarkanā krāsā izcelts transakcijas numurs. Šeit parādīta funkcija SELECT pg_back. Tā atgriež manu transakciju un šīs transakcijas ID.
Vēl viens moments: ja jums patīk šī prezentācija un vēlaties to palaist savā datu bāzē, varat sekot šai rozā krāsā izceltai saitei un lejupielādēt SQL šai prezentācijai. Varat to vienkārši palaist savā PSQL un visa prezentācija parādīsies jūsu ekrānā nekavējoties. Tajā nebūs krāsu, bet vismaz mēs to varēsim redzēt.

Šajā gadījumā redzam transakcijas ID. Tas ir numurs, kuru mēs tam piešķīrām. Ir arī cits transakcijas ID veids Postgres, ko sauc par virtuālo transakcijas ID.
Mums tas ir jāizprot. Tas ir ļoti svarīgi, citādi mēs nevarēsim saprast bloķēšanu Postgres.
Virtuālais transakcijas ID ir transakcijas ID, kas nesatur pastāvīgas vērtības. Piemēram, ja es palaistu SELECT komandu, visdrīzāk es nemainīšu datu bāzi un neko nebloķēšu. Tāpēc, kad mēs palaistam vienkāršu SELECT, mēs nedodam šai transakcijai pastāvīgu ID. Mēs piešķiram tikai virtuālo ID.
Tas uzlabo Postgres veiktspēju un attīrīšanas iespējas, tāpēc virtuālais transakcijas ID sastāv no diviem skaitļiem. Pirmais skaitlis pirms slīpsvītras ir backend ID. Bet pa labi mēs redzam vienkārši skaitītāju.

Tādēļ, ja es palaistu vaicājumu, tas norāda, ka backend ID ir 2.

Un, ja es palaistu virkni šādu transakciju, mēs redzam, ka skaitītājs katru reizi palielinās, kad es palaistu vaicājumu. Piemēram, kad es palaistu vaicājumu 2/10, 2/11, 2/12 utt.

Ņemiet vērā, ka šeit ir divas kolonnas. Pa kreisi mēs redzam virtuālo transakcijas ID – 2/12. Bet pa labi mums ir pastāvīgais transakcijas ID. Un šajā laukā nav nekā. Un šī transakcija nemodificē datu bāzi. Tāpēc es tai nepiešķiru pastāvīgu transakcijas ID.

Tiklīdz es palaistu komandu analizēt (ANALYZE), tas pats vaicājums man sniedz pastāvīgu transakcijas ID. Apskatiet, kā tas ir mainījies. Agrāk man nebija šī ID, tagad tas ir parādījies.

Tātad, šeit ir vēl viens vaicājums, vēl viena transakcija. Virtuālais transakcijas numurs – 2/13. Un, ja es pieprasīšu pastāvīgu transakcijas ID, tad, kad es palaistu vaicājumu, es to iegūšu.

Tātad, vēlreiz. Mums ir virtuālais transakcijas ID un pastāvīgais transakcijas ID. Vienkārši saprotiet šo momentu, lai izprastu Postgres uzvedību.

Pārejam pie trešā sadaļas. Šeit mēs vienkārši apskatīsim dažādus bloķēšanas veidus Postgres. Tas nav pārāk aizraujoši. Pēdējā sadaļa būs daudz interesantāka. Bet mums ir jāapsver pamata lietas, citādi mēs nesapratīsim to, kas notiks tālāk.
Mēs iziesim cauri šai sadaļai, mēs apskatīsim katru bloķēšanas veidu. Un es jums parādīšu piemērus, kā tie tiek iestatīti, kā tie darbojas, parādīšu jums dažus vaicājumus, kurus var izmantot, lai redzētu, kā darbojas bloķēšanas mehānisms Postgres.

Lai izveidotu vaicājumu un redzētu, kas notiek Postgres, mums ir jāizdod vaicājums system view. Šajā gadījumā sarkanā krāsā ir izcelta pg_lock. Pg_lock ir sistēmas tabula, kas mums norāda, kuras bloķēšanas šobrīd tiek izmantotas Postgres.
Tomēr man ir ļoti grūti parādīt jums pg_lock pašu, jo tas ir diezgan sarežģīti. Tāpēc es izveidoju view, kas parāda pg_locks. Tas arī veic man dažus uzdevumus, kas ļauj man labāk saprast. T. i., tas izslēdz manas bloķēšanas, manu paša sesiju utt. Tas ir vienkārši standarta SQL, un tas ļauj labāk jums parādīt, kas notiek.

Vēl viena problēma ir tā, ka šis view ir ļoti plašs, tāpēc man ir jāizveido otrais – lockview2.
Tas man rāda vēl vairāk kolonnas no tabulas. Un vēl viena, kas man rāda pārējās kolonnas. Tas ir diezgan sarežģīti, tāpēc es centos to parādīt pēc iespējas vienkāršāk.

Tātad, mēs izveidojām tabulu, kas saucas Lockdemo. Un mēs tajā izveidojām vienu rindu. Tā ir mūsu parauga tabula. Un mēs izveidosim sadaļas, lai vienkārši parādītu jums bloķēšanas piemērus.

Tātad, viena rinda, viena kolonna. Pirmais bloķēšanas veids saucas ACCESS SHARE. Tas ir vismazāk ierobežojošs bloķējums. Tas nozīmē, ka tas praktiski nesaskaras ar pārējām bloķēšanām.
Ja, ja, ja mēs vēlamies skaidri definēt bloķēšanu, mēs palaidīsim komandu «lock table». Tas skaidri bloķēs, t. i., piekļuves koplietošanas režīmā mēs palaidīsim lock table. Un, ja es palaidīšu PSQL fona režīmā, es tādējādi palaidīšu otro sesiju no savas pirmās sesijas. T. i., ko es šeit darīšu? Es pāreju uz citu sesiju un saku tai «parādi man lockview šim vaicājumam». Un šeit man ir AccessShareLock šajā tabulā. Tas ir tieši tas, ko es prasīju. Un viņš saka, ka bloķēšana ir piešķirta. Ļoti vienkārši.

Tālāk, ja mēs skatāmies uz otro kolonnu, tur nekas nav. Tās ir tukšas.

Un, ja es palaidīšu komandu «SELECT», tas ir implicitais (acīmredzamais) veids, kā pieprasīt AccessShareLock. Tāpēc es izlaistu savu tabulu un palaistu vaicājumu, un vaicājums atgriež vairākas rindas. Un vienā no rindām mēs redzam AccessShareLock. Tādējādi SELECT izraisa AccessShareLock tabulā. Un tas praktiski nesaskarās ar neko, jo tā ir zema līmeņa bloķēšana.

Ko darīt, ja es palaidīšu SELECT un man būs trīs dažādas tabulas? Agrāk es palaidu tikai vienu tabulu, tagad es palaidu trīs: pg_class, pg_namespace un pg_attribute.

Un tagad, kad es skatos uz vaicājumu, es redzu 9 AccessShareLocks trijās tabulās. Kāpēc? Zilā krāsā izcelti trīs tabulas: pg_attribute, pg_class, pg_namespace. Bet jūs varat arī redzēt, ka visiem indeksam, kas definēts caur šīm tabulām, ir arī AccessShareLock.
Un tas ir bloķējums, kas praktiski nesaskaras ar citiem. Un viss, ko viņš dara, ir, ka viņš vienkārši neļauj mums iztīrīt tabulu, kamēr mēs to izvēlamies. Tas ir jēgpilni. T. i., ja mēs izvēlamies tabulu, tā tajā brīdī izzūd, tad tas nav pareizi, tāpēc. AccessShare – tas ir zema līmeņa bloķējums, kas saka mums «neizdzēsiet šo tabulu, kamēr es strādāju».Pamatā tas ir viss, ko viņš dara.

ROW SHARE – tas ir bloķējums, kas nedaudz atšķiras.

Ņemsim piemēru. SELECT ROW SHARE ir veids, kā bloķēt katru rindu atsevišķi.Tādējādi neviens nevar tās izdzēst vai mainīt, kamēr mēs tās skatām.
Tātad, ko dara SHARE LOCK? Mēs redzam, ka transakcijas ID 681 attiecās uz SELECT. Un tas ir interesanti. Kas šajā gadījumā notika? Pirmo reizi mēs redzam numuru laukā „Lock”. Mēs ņemam transakcijas ID, un tas saka, ka tā ekskluzīvi bloķē. Viss, ko tā dara, viņa saka, ka man ir rinda, kas tehniski ir bloķēta kaut kur tabulā. Bet nepasaka, kur tieši. Nedaudz vēlāk mēs to apskatīsim detalizētāk.

Šeit mēs sakām, ka bloķēšana tiek izmantota mums.

Tātad, ekskluzīvā bloķēšana skaidri (pārskatāmi) norāda, ka tā ir ekskluzīva. Un, ja jūs dzēšat rindu šajā tabulā, tad tas tiešām notiks, kā jūs varat redzēt.

SHARE EXCLUSIVE – tā ir ilgāka bloķēšana.

Tas (ANALYZE) ir analīzes komanda, kas tiks izmantota.

SHARE LOCK – jūs varat skaidri bloķēt share režīmā.

Jūs varat arī izveidot unikālu indeksu. Un tur jūs varat redzēt SHARE LOCK, kas ir tā daļa. Un tas bloķē tabulu un uzliek tai SHARE LOCK bloķēšanu.
Pēc noklusējuma SHARE LOCK tabulai nozīmē, ka citi var lasīt tabulu, bet neviens to nevar modifikēt. Un tieši tas notiek, kad jūs izveidojat unikālu indeksu.
Ja es izveidoju unikālu concurrently indeksu, man būs cits bloķēšanas veids, jo, kā jūs atceraties, concurrently indeksu izmantošana samazina bloķēšanas prasības. Un, ja es izmantoju parasto bloķēšanu, parasto indeksu, tad es tādējādi novēršu rakstīšanu indeksā tabulas izveides laikā. Ja es izmantoju concurrently indeksu, man jāizmanto cits bloķēšanas veids.

SHARE ROW EXCLUSIVE – atkal to var noteikt skaidri (pārskatāmi).

Vai arī varam izveidot noteikumu, t.i., ņemt kādu konkrētu gadījumu, kad tas tiks izmantots.

EXCLUSIVE bloķēšana nozīmē, ka neviens cits nevarēs mainīt tabulu.

Šeit mēs redzam dažādus bloķēšanas veidus.

ACCESS EXCLUSIVE, piemēram, ir bloķēšanas komanda. Piemēram, ja jūs veicat CLUSTER table, tas nozīmēs, ka neviens nevarēs tajā rakstīt. Un tas bloķē ne tikai pašu tabulu, bet arī indeksus.

Tas ir otrais ACCESS EXCLUSIVE bloķēšanas lapa, kurā mēs konkrēti redzam, ko tā bloķē tabulā. Tā bloķē atsevišķas tabulas rindas, kas ir diezgan interesanti.
Šī ir visa pamatinformācija, ko es vēlējos sniegt. Mēs runājām par bloķēšanām, par darījumu ID, runājām par virtuālajiem darījumu ID, par pastāvīgajiem darījumu ID.

Tagad mēs aplūkosim bloķēšanas piemērus. Tas ir visinteresantākais posms. Mēs izskatīsim ļoti interesantus gadījumus. Un mana uzdevums šajā prezentācijā ir sniegt jums labāku izpratni par to, ko Postgres patiesībā dara, kad tas mēģina bloķēt dažādas lietas. Man šķiet, ka tas ļoti labi prot bloķēt atsevišķus elementus.
Apskatīsim konkrētus piemērus.

Sāksim ar tabulām un vienu rindu tabulā. Kad es ko ievietoju, man parādās ExclusiveLock, darījuma ID un ExclusiveLock uz tabulu.

Bet kas notiks, ja es ievietošu vēl divas rindas? Tagad mūsu tabulā ir trīs rindas. Es ievietoju vienu rindu un saņemu šādu rezultātu. Un, ja es ievietošu vēl divas rindas, kas šeit ir dīvaini? Šeit ir dīvainība, jo es pievienoju trīs rindas šai tabulai, bet man joprojām ir divas rindas bloķēšanā. Un tas ir, būtībā, Postgres pamata uzvedība.
Daudzi domā, ka, ja datos jūs bloķējat 100 rindas, jums būs nepieciešams izveidot 100 skatījumu bloķēšanām. Ja es bloķēšu uzreiz 1 000 rindas, tad man būs vajadzīgi 1 000 šādu pieprasījumu. Un, ja man jānobloķē miljons vai miljards. Bet, ja mēs to darīsim, tad tas nedarbosies ļoti labi. Ja esat izmantojuši sistēmu, kas veido bloķēšanas ierakstus katrai atsevišķai rindai, tad saprotiet, ka tas ir sarežģīti. Jo jums nekavējoties jānosaka bloķēšanas tabula, kas var pārpildīties, bet Postgres tā nedara.
Un šajā slīdī ir ļoti svarīgi, ka šeit skaidri parādīts, ka pastāv vēl viena sistēma, kas darbojas iekš MVCC, kas bloķē atsevišķas rindas. Tādēļ, kad jūs bloķējat miljardiem rindu, Postgres neveido miljardus atsevišķu bloķēšanas komandu. Un tas ļoti labi ietekmē veiktspēju.

Kā ir ar atjauninājumiem? Es pašlaik atjauninu rindas, un jūs varat pamanīt, ka tas uzreiz veica divas dažādas darbības. Tas vienlaicīgi bloķēja tabulu, bet arī bloķēja indeksu. Un tam bija nepieciešams bloķēt indeksu, jo šajā tabulā ir unikālie ierobežojumi. Mēs vēlamies pārliecināties, ka neviens to nemaina, tāpēc mēs to bloķējam.

Ko darīt, ja es gribu atjaunināt divas rindas? Mēs redzam, ka tas uzvedas tāpat. Mēs veicam divreiz vairāk atjauninājumu, bet bloķēto rindu skaits paliek tāds pats.
Ja jūs interesē, kā Postgres to dara, jums jāizdara uzmanība uz manām lekcijām par MVCC, lai uzzinātu, kā Postgres iekšēji apzīmē šīs rindas, kuras tas maina. Un Postgres ir veids, kā to izdarīt, bet tas to nedara tabulu bloķēšanas līmenī, bet gan efektīvākā un zemākā līmenī.

Bet, ja es gribu kaut ko izdzēst? Ja es dzēšu, piemēram, vienu rindu un man joprojām ir manas divas bloķēšanas, un pat ja es vēlētos tās visas izdzēst, tās joprojām būtu klāt.

Piemēram, ja es gribu ievietot 1 000 rindu un tad vai nu izdzēst, vai pievienot 1 000 rindu, tad tās individuālās rindas, kuras es pievienoju vai mainu, netiek ierakstītas šeit. Tās tiek ierakstītas uz zemāka līmeņa iekšējos datos. Un lekcijā par MVCC es par to runāju detaļās. Bet ir ļoti svarīgi, kad jūs analizējat bloķējumus, pārliecināties, ka jums ir bloķējums tabulas līmenī un ka jūs neredzat, kā tiek ierakstītas atsevišķas rindas.

Kā ir ar eksplicīto bloķēšanu?

Ja es nospiežu "atjaunināt", tad man ir divas bloķētas rindas. Un, ja es tās visas iezīmēju un nospiedu "atjaunināt visur", man joprojām paliek divi bloķējumi.

Mēs neveidojam atsevišķas ierakstus katrai atsevišķai rindai. Jo tad sniegums samazinās, varētu būt pārāk daudz. Un mēs varam nonākt nepatīkamajā situācijā.

Un tas pats attiecībā uz shared; mēs varam veikt to 30 reizes.

Mēs atjaunojam mūsu tabulu, visu izdzēšam, pēc tam atkal ievietojam vienu rindu.

Vēl viena uzvedība, ko jūs varat redzēt Postgres, ir ļoti labi zināma un vēlama uzvedība – jūs varat veikt gan update, gan select. Un jūs varat to darīt vienlaikus. Select nebloķē update, un arī otrādi. Mēs sakām, ka lasītājs nebloķē to, kurš raksta, bet tas, kurš raksta, nebloķē lasītāju.
Es jums parādīšu šī piemēra. Es pašlaik veicu izvēli. Mēs vēlāk veiksim INSERT. Un jūs tad varēsiet redzēt – 694. Jūs varēsiet redzēt darījuma ID, kas veica šo ievietošanu. Un tā ir šī darbība.

Ja tagad paskatīšos uz savu aizmugurējo ID, tad tas ir kļuvis – 695.

Un es varu redzēt, ka 695 parādās manā tabulā.

Ja es šeit veicu atjaunināšanu tā, tad es iegūstu citu gadījumu. Šajā gadījumā 695 ir ekskluzīva bloķēšana, bet update ir tāda pati uzvedība, tikai starp tām nerodas konflikts, kas ir pietiekami neparasti.
Un jūs varat pamanīt, ka augšpusē – tas ir ShareLock, bet apakšā – tas ir ExclusiveLock. Un abas darījumi ir sasnieguši rezultātu.
Un jums jāklausās manā prezentācijā par MVCC, lai saprastu, kā tas notiek. Bet tas ir ilustrācija tam, ka jūs varat to darīt vienlaikus, proti, vienlaikus veikt SELECT un UPDATE.

Lūdzu, mēs izslēgsim un vēlreiz veiksim vienu operāciju.

Ja jūs mēģināsiet vienlaikus uzsākt divus update uz vienu un to pašu rindu, tad tas tiks bloķēts. Un atcerieties, es teicu, ka lasītājs nebloķē rakstītāju, bet rakstītājs bloķē lasītāju, bet viens rakstītājs bloķē otru rakstītāju. Proti, mēs nevaram pieļaut, lai divi cilvēki vienlaikus atjauninātu to pašu rindu. Jāmācās gaidīt, kamēr vienam no viņiem beidzas process.

Un lai to ilustrētu, es paskatīšos uz Lockdemo tabulu. Un mēs aplūkosim vienu rindu. Par darījumu 698.
Mēs to atjauninājām līdz 2. 699 – tas ir pirmais atjauninājums. Un tas ir izgājis veiksmīgi vai atrodas gaidošā darījuma stāvoklī un gaida, kad mēs apstiprināsim vai atcelsim.

Bet skatīt uz ko citu – 2/51 – tā ir mūsu pirmā transakcija, mūsu pirmā sesija. 3/112 – tas ir otrais pieprasījums, kas parādījās augšpusē un mainīja šo vērtību uz 3. Un, ja pamanāt, tad augšējais ir bloķējis pats sevi, kas ir 699. Bet 3/112 nesniedza bloķēšanu. Kolonnā Lock_mode rakstīts, ka tas gaida. Tas gaida 699. Un, ja paskatāties, kur ir 699, tas ir augstāk. Un ko izdarīja pirmā sesija? Tā izveidoja ekskluzīvu bloķēšanu uz savu pašu transakcijas ID. Tā tas darbojas Postgres. Tas bloķē savu pašu transakcijas ID. Un, ja vēlaties gaidīt, kamēr kāds apstiprina vai atceļ, jāgaida, kamēr ir gaidoša transakcija. Tāpēc mēs varam redzēt dīvainu rindiņu.
Apskatīsim vēlreiz. Kreisajā pusē mēs redzam mūsu apstrādes ID. Otrajā kolonnā mēs redzam mūsu virtuālo transakcijas ID, bet trešajā redzam lock_type. Ko tas nozīmē? Patiesībā, tas saka, ka tas bloķē transakcijas ID. Bet atzīmējiet, ka visās rindās apakšā rakstīts relation. Tāpēc jums ir divu veidu bloķēšana tabulā. Ir relation bloķēšana. Un arī ir transactionid bloķēšana, kur jūs bloķējat paši, tas ir tieši tas, kas notiek pirmajā rindā vai pašā apakšā, kur transactionid, kur mēs gaidām, lai 699 pabeigtu savu darbību.
Es skatos, kas šeit notiek. Un šeit vienlaikus notiek divas lietas. Jūs skatāties uz blokādi pēc transakcijas ID pirmajā rindā, kas bloķē pati sevi. Un tas bloķē pati sevi, lai liktu cilvēkiem gaidīt.
Ja jūs paskatāties uz 6. rindu, tad tā pati ieraksts kā pirmajā. Un tāpēc transakcija 699 ir bloķēta. 700 arī pašbloķējas. Un pēc tam apakšējā rindā jūs redzēsiet, ka mēs gaidām, kad 699 pabeigs savu darbību.

Un lock_type, tuple jūs redzat skaitļus.

Jūs varat redzēt, ka tas ir 0/10. Un tas ir lapu numurs, un arī šī konkrētā rinda offset.

Un jūs redzat, ka tas kļūst 0/11, kad mēs atjaunojam.

Bet patiesībā – tas ir 0/10, jo notiek šīs darbības gaidīšana. Mums ir iespēja paskatīties, ka šī ir tā rinda, kuru es gaidu, lai apstiprinātu.

Kad mēs to apstiprinājām un nospiedām commit, un kad atjauninājums beidzās, mēs atkal iegūsim šo rezultātu. Transakcija 700 ir vienīgais bloķējums, tā vairs nevienu negaida, jo tā ir apstiprināta. Tā vienkārši gaida, līdz transakcija noslēgsies. Tiklīdz 699 beidzas, mēs vairs neko negaidām. Un tagad transakcija 700 saka, ka viss ir kārtībā, ka visi nepieciešamie bloķējumi ir pieejami visās atļautajās tabulās.

Lai vēl vairāk sarežģītu to, mēs izveidosim vēl vienu skatu, kas šoreiz nodrošinās mums hierarhiju. Es negaidu, ka jūs sapratīsiet šo pieprasījumu. Taču tas dos mums skaidrāku priekšstatu par to, kas notiek.

Tas ir rekursīvs skats, kuram ir arī vēl viena sadaļa. Un tas vēlreiz apvieno visu kopā. Ļaujiet to izmantot.

Ko, ja mēs veiksim trīs vienlaicīgus atjauninājumus un teiksim, ka rinda tagad ir vienāda ar trim. Un mēs nomainīsim 3 uz 4.

Un lūk, mēs redzam 4. Un transakcijas ID 702.

Un tad es nomainīšu 4 uz 5. Un 5 uz 6, un 6 uz 7. Un es rindoju cilvēku rindā, kuri gaidīs, lai šī viena transakcija noslēgtos.

Un viss kļūst skaidrs. Kāda ir pirmā rinda? Tas ir 702. Tas ir transakcijas ID, kas sākotnēji noteica šo vērtību. Un ko man raksta kolonnā Granted? Man ir atzīmes f. Tie ir mani atjauninājumi (5, 6, 7), kas nevar tikt apstiprināti, jo mēs gaidām, lai transakcijas ID 702 pabeigtos. Tur ir transakcijas ID bloķējums. Un tā iegūstam 5 transakciju bloķējumus ID.
Un, ja jūs paskatīsieties uz 704, uz 705, tad tur vēl nekas nav uzrakstīts, jo viņi vēl nezina, kas notiek. Viņi vienkārši raksta, ka nav ne jausmas, kas notiek. Un viņi vienkārši aizmigs, jo gaida, kad kāds pabeigs un pamodinās viņus, kad parādīsies iespēja nomainīt rindu.

Tā tas izskatās. Ir skaidrs, ka viņi visi gaida 12. rindu.

Tas ir tas, ko mēs redzējām šeit. Lūk, 0/12.

Tātad, kad pirmā transakcija ir apstiprināta, jūs varat redzēt, kā darbojas hierarhija. Un tagad viss kļūst skaidrs. Viņi visi kļūst tīri. Un viņi faktiski vēl joprojām gaida.

Lūk, kas notiek. 702 veic kommitu. Un tagad 703 saņem šo rindu bloķēšanu, un tad 704 sāk gaidīt, kad 703 veiks kommitu. Un 705 arī to gaida. Un kad viss tas beidzas, viņi paši sevi atbrīvo. Un es vēlētos uzsvērt, ka visi izkārtojas rindā. Un tas ļoti atgādina situāciju ar sastrēgumu, kad visi gaida pirmo mašīnu. Pirmā mašīna apstājās, un visi izkārtojas garā rindā. Pēc tam tā pārvietojas, tad nākamā mašīna var iziet uz priekšu un saņemt savu bloķēšanu utt.

Un, ja jums šķita, ka tas nav pietiekami sarežģīti, tad tagad runāsim par deadlocks. Es nezinu, kurš no jums ar to ir saskāries. Tas ir diezgan izplatīts problēma datubāzēs. Bet deadlocks ir tāds gadījums, kad viena sesija gaida, lai kaut ko izpildītu otra sesija. Bet šajā brīdī otra sesija gaida, lai pirmā sesija kaut ko izpildītu.
Un, piemēram, ja Jānis saka: „Dosi man kaut ko”, bet es saku: „Nē, es to došu tikai tad, ja tu man dosi kaut ko citu”. Un viņš saka: „Nē, es tev to nedošu, ja tu man nedosi”. Un mēs nonākam mirstīgā bloķēšanas situācijā. Es esmu pārliecināts, ka Jānis tā nesaprastu, bet jūs saprotat jēgu, ka mums ir divi cilvēki, kas vēlas kaut ko iegūt, un viņi nav gatavi to nodot, kamēr otrs cilvēks viņiem nedos to, ko viņi vēlas. Un te nav risinājuma.
Pamats, jūsu datubāzei tas jāidentificē. Un tad ir jāizdzēš vai jāsit vienu no sesijām, jo citādi tās paliks tur uz visiem laikiem. Un mēs to redzam datubāzēs, mēs to redzam operētājsistēmās. Un visur, kur mums ir paralēli procesi, tas var notikt.

Un tagad mēs izveidosim divus deadlocks. Mēs izveidosim 50 un 80. Pirmajā rindā es veicu atjaunināšanu no 50 uz 50. Man iznāk darījuma numurs 710.

Un pēc tam es mainīšu 80 uz 81, un 50 uz 51.

Un lūk, kā tas izskatīsies. Un tāpēc 710 ir rindu bloķēšana, bet 711 gaida apstiprinājumu. Mēs to redzējām, kad atjauninājām. 710 ir mūsu rindu īpašnieks. Bet 711 gaida, lai 710 pabeigtu darījumu.

Un tur pat ir uzrakstīts, uz kādā tieši rindā mums notiek deadlocks. Un šeit sāk kļūt dīvaini.

Tagad mēs atjauninām 80 uz 80.

Un tā ir vieta, kur sākas deadlocks. 710 gaida atbildi no 711, bet 711 gaida atbildi no 710. Un tam nebūs labas beigas. Un nav izejas. Viņi gaidīs atbildes viens no otra.

Un tas viss sāks aizkavēties. Un mēs to negribam.

Un Postgres ir veidi, kā pamanīt, kad tas notiek. Un kad tas notiek, tad jūs saņemat šādu kļūdu. Un no tā ir skaidrs, ka viens process gaida SHARE LOCK no otra procesa, t. i., tas, kuru bloķē 711. process. Un tas process gaidīja, lai tiktu piešķirts SHARE LOCK uz kādu transakcijas ID un tika bloķēts ar kādu citu procesu. Tāpēc šeit ir mirstīga bloķēšana.

Bet vai ir iespējama trīsbiedru deadlocks? Vai tas ir iespējams? Jā.

Mēs ievadam šos skaitļus tabulā. Mēs mainām 40 uz 40, mēs veicam bloķēšanu.

Mēs mainām 60 uz 61, 80 uz 81.

Un tad mēs mainām 80, un tad - bums!

Un 714 tagad gaida 715. 716. gaida 715. Un ar to vairs neko nevar izdarīt.

Šeit jau nav divi cilvēki, šeit jau ir trīs cilvēki. Es gribu kaut ko no tevis, šis vēlas kaut ko no trešā cilvēka, bet trešais cilvēks vēlas kaut ko no manis. Un mēs nonākam trīsbiedru gaidīšanā, jo visi gaidām, kamēr cits cilvēks pabeigs to, kas viņam jādara.

Un Postgres zina, kurā rindā tas notiek. Tāpēc viņš izsniegs jums sekojošo ziņojumu, kas parāda, ka jums ir problēma, kur trīs ievades bloķē viena otru. Un šeit nav ierobežojumu. Tas var būt gadījumā, kad 20 ieraksti bloķē viens otru.

Nākamā problēma ir serializable.

Ja ir īpaša serializable bloķēšana.

Un atgriežamies pie 719. Tam ir pilnīgi normāls iznākums.

Un jūs varat nospiest, lai izveidotu transakciju no serializable.

Un jūs saprotat, ka tagad jums ir cita veida bloķēšana SA - tas nozīmē serializable.


Tāpēc mums ir jauna bloķēšanas veids, ko sauc par SARieadLock, kas ir sērijveida bloķēšana un ļauj ievadīt sērijas numurus.

Un jūs arī varat ievietot unikālas indeksus.

Šajā tabulā mums ir unikālie indeksi.

Tāpēc, ja es šeit ievadu skaitli 2, tāpēc man ir 2. Bet augšā es ievietoju vēl vienu 2. Un jūs varat redzēt, ka 721. ir ekskluzīvs bloķējums. Bet tagad 722 gaida, lai 721 pabeigtu savu operāciju, jo viņš nevar ievietot 2, pirms nezin, kas notiks ar 721.

Un ja mēs veicam subtransaction.

Šeit mums ir 723.

Ja, ja saglabājam punktu un pēc tam to atjauninām, tad mums rodas jauns transakcijas ID. Tas ir vēl viens uzvedības raksturs, par kuru jums jāzina. Ja mēs to atgriežam, tad transakcijas ID ir prom. 724 ir prom. Bet tagad mums parādās 725.
Un ko es šeit cenšos darīt? Es cenšos jums parādīt piemērus neparastām bloķēšanām, kuras varat sastapt: vai tas būtu serializable bloķējumi vai SAVEPOINT – tie ir atšķirīgi bloķējumu veidi, kas parādīsies bloķējumu tabulā.

Tas ir eksplicītu (skaidru) bloķējumu izveide, kuriem ir pg_advisory_lock.

Un jūs redzat, ka bloķējuma veids šeit ir norādīts kā advisory. Un šeit sarkanā krāsā ir uzrakstīts «advisory». Un jūs varat vienlaikus tā bloķēt ar pg_advisory_unlock.

Un noslēdzot, es vēlētos jums parādīt vēl vienu nogalinošu lietu. Es izveidošu vēl vienu veidu. Bet es savienošanu tabulu pg_locks ar tabulu pg_stat_activity. Un kāpēc es to vēlos darīt? Tāpēc, ka tas ļaus man apskatīt un redzēt visas pašreizējās sesijas un redzēt, kādas tieši bloķēšanas tās gaida. Un tas ir diezgan interesanti, kad mēs apvienojam bloķējumu tabulu un pieprasījumu tabulu.

Un šeit mēs izveidojam pg_stat_view.

Un mēs atjauninām rindu par vienu. Un šeit mēs redzam 724. Pēc tam mēs atjauninām mūsu rindu līdz trim. Un ko jūs tagad redzat? Tie ir pieprasījumi, tas ir, jūs redzat visu pieprasījumu sarakstu, kas ir uzskaitīts kreisajā kolonnā. Un pēc tam labajā pusē jūs varat redzēt bloķējumus un to, ko tie rada. Un tas var būt saprotamāk jums, lai jums nebūtu jāatgriežas katru reizi pie katras sesijas un jāskatās – vai vajag to pievienot vai ne. Mums to paveic.
Vēl viena funkcija, kas ir ļoti noderīga – tā ir pg_blocking_pidsJūs, iespējams, to nekad neesat dzirdējuši. Ko tas dara? Tas ļauj mums pateikt, ka šai sesijai 11740, kādi precīzi ID procesi tā gaida. Un jūs varat redzēt, ka 11740 gaida 724. Un 724 ir pašā augšā. Savukārt 11306 ir jūsu procesa ID. Pamatā šī funkcija pārlūko jūsu bloķēšanas tabulu. Un es zinu, ka tas ir nedaudz sarežģīti, taču jums tas izdodas saprast. Pamatā šī funkcija pārskata šo bloķēšanas tabulu un cenšas atrast, kur atrodas šis procesa ID, ņemot vērā tās bloķēšanas, kuras tā gaida. Tā arī mēģina aprēķināt, kāds tieši procesa ID ir tam procesam, kurš gaida bloķēšanas. Tādējādi jūs varat palaist šo funkciju. pg_blocking_pids.
Un tas ir ļoti noderīgi. Mēs to pievienojām tikai ar 9.6 versiju, tāpēc šai funkcijai ir tikai 5 gadi, bet tā ir ļoti, ļoti noderīga. Tas pats attiecas uz otro pieprasījumu. Tas parāda tieši to, ko mums vajag redzēt.

Tas ir tas, par ko es gribēju ar jums runāt. Un kā es gaidīju, mēs izmantojām visu mūsu laiku, jo bija tik daudz slaidu. Un slaidi ir pieejami lejupielādei. Es vēlētos pateikties jums, ka bijāt šeit. Esmu pārliecināts, ka jums patiks pārējā konference, liels paldies!
Jautājumi:
Piemēram, ja es cenšos atjaunināt rindas, bet otrais sesija cenšas dzēst visu tabulu. Cik es saprotu, tur vajadzētu būt kaut kas līdzīgs intent lock. Vai Postgres ir tāds?

Atgriežamies pie paša sākuma. Iespējams, jūs atceraties, ka, kad jūs darāt jebko, piemēram, kad veicat SELECT, mēs izsniedzam AccessShareLock. Un tas novērš tabulas dzēšanu. Tāpēc, ja jūs, piemēram, vēlaties atjaunināt rindu tabulā vai dzēst rindu, tad kāds nevar dzēst visu tabulu vienlaicīgi ar to, jo jūs turat šo AccessShareLock virs visas tabulas un virs rindas. Un tiklīdz esat pabeiguši, viņi var to dzēst. Bet, kamēr jūs tieši kaut ko tur maināt, viņi to nevar izdarīt.
Apskatīsim vēlreiz. Pāriesim pie dzēšanas piemēra. Un jūs redzat, kā rindā ir ekskluzīvs bloķējums visai tabulai.
Tas izskatīsies kā ekskluzīvs bloķējums, pareizi?
Jā, tas izskatās tā. Es saprotu, par ko jūs runājat. Jūs sakāt, ka, ja es izpildīšu SELECT, man būs ShareExclusive, un tad es to pārvērstu Row Exclusive, vai tas kļūst par problēmu? Bet pārsteidzoši tas nerada problēmas. Tas izskatās kā bloķēšanas pakāpes palielināšana, bet būtībā man ir bloķēšana, kas novērš dzēšanu. Un tagad, kad es šo bloķēšanu padaru spēcīgāku, tā joprojām novērš dzēšanu. Tātad, tas nav tā, ka es kāptu augstāk. Proti, tas novērsa to arī tad, kad tas bija zemākā līmenī, tāpēc, kad es paaugstinu tā līmeni, tas joprojām novērš tabulas dzēšanu.
Es saprotu, par ko jūs runājat. Šeit nav situācijas, kad bloķēšanas pakāpe palielinās, kur jūs mēģināt atteikties no vienas bloķēšanas, lai ieviestu spēcīgāku. Šeit tas vienkārši visur palielina šo novēršanu, tādēļ tas nerada konfliktus. Bet tas ir labs jautājums. Liels paldies, ka to uzdevāt!
Ko mums nepieciešams darīt, lai izvairītos no deadlock situācijas, kad mums ir daudz sesiju, liels lietotāju skaits?
Postgres automātiski pamanīs deadlock situācijas. Un automātiski izdzēsīs vienu no sesijām. Vienīgais veids, kā izvairīties no mirušajām bloķēšanām, ir bloķēt cilvēkus tajā pašā secībā. Tātad, kad jūs skatāties uz savu lietotni, bieži vien deadlock iemesls ... Iedomājieties, ka es gribu bloķēt divas dažādas lietas. Viens lietotne bloķē tabulu 1, bet otra lietotne bloķē 2, un pēc tam tabulu 1. Un visvienkāršākais veids, kā izvairīties no deadlocks, ir paskatīties uz savu lietotni un mēģināt nodrošināt, lai bloķēšana notiktu tajā pašā secībā visās lietotnēs. Un tas parasti novērš 80% problēmu, jo dažādi cilvēki raksta šīs lietotnes. Un, ja jūs tos bloķējat tajā pašā secībā, tad jūs nesaskarsities ar deadlock situāciju.
Liels paldies par jūsu runu! Jūs runājāt par vacuum full un, ja es pareizi saprotu, vacuum full deformē ierakstu kārtību atsevišķā glabāšanā, tāpēc esošie ieraksti paliek nemainīgi. Un kāpēc vacuum full iegūst ekskluzīvas bloķēšanas piekļuvi un kāpēc tas nonāk konfliktā ar rakstīšanas operācijām?
Tas ir labs jautājums. Iemesls ir tāds, ka vacuum full aizņem tabulu. Un mēs būtībā izveidojam jaunu tabulas versiju. Tāpēc tabula būs jauna. Rezultātā tā būs pilnīgi jauna tabulas versija. Un problēma ir tāda, ka, kad mēs to darām, mēs nevēlamies, lai cilvēki to lasa, jo mums ir nepieciešams, lai viņi redzētu jauno tabulu. Tāpēc tas saistās ar iepriekšējo jautājumu. Ja vienlaikus varētu lasīt, mēs nevarētu to pārvietot un novirzīt cilvēkus uz jauno tabulu. Mums būtu jāgaida, lai katrs pabeigtu lasīt šo tabulu, un tāpēc, būtībā, ir situācija ar ekskluzīvu bloķēšanu.
Mēs vienkārši sakām, ka mēs bloķējam no paša sākuma, jo mēs zinām, ka pašā beigās mums būs nepieciešama ekskluzīva bloķēšana, lai pārvietotu visus uz jauno kopiju. Tāpēc potenciāli mēs varam to ļaut. Un mēs to darām ar vienlaicīgu indeksēšanu. Bet to ir daudz grūtāk izdarīt. Un tas ļoti cieši attiecas uz jūsu iepriekšējo jautājumu par ekskluzīvu bloķēšanu.
Vai ir iespējams pievienot bloķēšanas laika ierobežojumu Postgres? Oracle es varu, piemēram, uzrakstīt "izvēlēt atjaunināšanai" un gaidīt 50 sekundes līdz atjaunināšanai. Tas bija labi lietotnei. Bet Postgres man jāveic nekavējoties un nevienu mirkli negaidot, vai jāgaida līdz noteiktam laikam.
Jā, jūs varat izvēlēties laika ierobežojumu jūsu bloķēšanai, uz jūsu locks. Jūs arī varat izdot komandu no way, kas būs ..., ja jūs nevarēsiet nekavējoties iegūt bloķēšanu. Tātad vai nu bloķēšanas laika ierobežojums, vai kas cits, kas to atļaus. Tas nav izdarāms sintaktiskā līmenī. Tas tiek darīts kā mainīgais serverī. Dažreiz to nevar izmantot.
Vai jūs varat atvērt 75. slaidu?
Jā.

Un mans jautājums ir šāds: kāpēc abi atjaunināšanas procesi gaida 703?
Un tas ir lielisks jautājums. Es nesaprotu, kāpēc Postgres to dara. Bet, kad tika izveidots 703, tas gaidīja 702. Un, kad parādās 704 un 705, šķiet, ka tie nezina, ko gaida, jo tur vēl nekas nav. Un Postgres to dara šādi: kad jūs nevarat iegūt bloķēšanu, tas raksta: "Kāds jēga jūs apstrādāt?", jo jūs jau gaidāt kādu. Tāpēc ļausim tam karājamies gaisā, tas vispār to neatjauno. Bet kas šeit notika? Tiklīdz 702 pabeidz procesu un 703 saņem savu bloķēšanu, sistēma atgriežas atpakaļ. Un saka, ka tagad mums ir divi cilvēki, kas gaida. Un tad ļausim viņiem abus atjaunot kopā. Un norādīsim, ka abi gaida.
Es nezinu, kāpēc Postgres tā dara. Bet ir problēma, ko sauc par f…. Man šķiet, ka tas nav termins krieviski. Tas ir, kad visi gaida vienu bloķēšanu, pat ja ir 20 instanču, kas gaida bloķēšanu. Un pēkšņi viņi visi vienlaikus pamostas. Un visi sāk mēģināt reaģēt. Bet sistēma dara tā, ka visi gaida 703. Jo viņi visi gaida, un mēs viņus uzreiz visus izkārtojam rindā. Un, ja parādās jebkurš cits jauns pieprasījums, kas tika veidots pēc tam, piemēram, 707, tad tur atkal būs tukšums.
Un man šķiet, ka tas tiek darīts, lai varētu teikt, ka šajā posmā 702 gaida 703, bet visiem, kas nāks pēc tam, nebūs nekādas ieraksta šajā laukā. Bet, tiklīdz pirmais gaidītājs pamet, visi tie, kas gaidīja šajā brīdī līdz atjaunošanai, saņem to pašu marķieri. Un tāpēc, man šķiet, tas ir izdarīts, lai mēs varētu apstrādāt secībā, lai tie būtu pareizi sakārtoti.
Es vienmēr uz to skatījos kā uz diezgan dīvainu fenomenu. Jo šeit, piemēram, mēs viņus nemaz neuzskaitām. Bet, man šķiet, katru reizi, kad mēs piešķiram jaunu bloķēšanu, mēs skatāmies uz visiem, kas ir gaidīšanas procesā. Tad mēs viņus visus izkārtojam rindā. Un tad jebkurš jauns, kas nāk, nokļūst rindā tikai tad, kad nākamais cilvēks ir pabeidzis apstrādi. Ļoti labs jautājums. Liels paldies par jautājumu!
Man šķiet, daudz loģiskāk, ja 705 gaida 704.
Tomēr šeit ir problēma. Tehniski jūs varat modināt vai nu to, vai to. Tādēļ mēs modināsim vienu vai otru. Bet kas notiek sistēmas darbībā? Jūs redzat, ka 703 visaugšpusē bloķēja savu paša transakcijas ID. Tieši tā Postgres darbojas. Un 703 tiek bloķēts ar savu paša transakcijas ID, tāpēc, ja kāds vēlas gaidīt, viņš gaidīs 703. Un būtībā 703 pabeidz. Un tikai pēc tā pabeigšanas kāds no procesiem tiek modināts. Un mēs nezinām, kurš process tas būs. Pēc tam mēs pakāpeniski viss apstrādājam. Bet nav skaidrs, kurš tieši process tiek modināts pirmais, jo tas var būt jebkurš no šiem procesiem. Būtībā mums bija plānotājs, kurš teica, ka tagad varam modināt jebkuru no šiem procesiem. Mēs vienkārši izvēlamies vienu nejauši. Tādēļ abiem no tiem jābūt atzīmētiem, jo mēs varam modināt jebkuru no tiem.
Un problēma ir tāda, ka mums ir CP-bezgala. Tādēļ ir ļoti iespējams, ka mēs varam modināt vēlāko. Un, piemēram, ja mēs modinām vēlāko, gaidīsim to, kurš tikko ieguva bloķēšanu, tāpēc mēs nosakām, kurš tieši tiks modināts pirmais. Mēs vienkārši izveidojam tādu situāciju, un sistēma tos modinās nejaušā secībā.
Ir . Aplūkojiet, tie arī ir interesanti un noderīgi. Tematika, protams, ir neticami sarežģīta. Liels paldies, Brūsam!
Avots: habr.com
