Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Razlaga poročila iz leta 2020 Brucea Momjiana "Odklepanje upravljalnika zaklepov Postgres".

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

(Opomba: Vse SQL poizvedbe s diapozitivov lahko dobite na tej povezavi: http://momjian.us/main/writings/pgsql/locking.sql)

Pozdravljeni! Odlično je ponovno biti tukaj v Rusiji. Opravičujem se, da se nisem mogel udeležiti prejšnjega leta, vendar imata Ivan in jaz v tem letu velike načrte. Upam, da bom tukaj veliko bolj pogosto. Rad imam obiske v Rusiji. Obiskal bom Tjumen in Tver. Veselim se, da bom obiskal ta mesta.

Ime mi je Bruce Momjian. Delam v podjetju EnterpriseDB in z Postgresom delam že več kot 23 let. Živim v Filadelfiji v ZDA. Potujem približno 90 dni na leto. Udeležim se približno 40 konferenc. Moj spletni stran, ki vsebuje diapozitive, ki vam jih bom zdaj pokazal. Zato si jih lahko po konferenci prenesete z moje osebne spletne strani. Na voljo je tudi približno 30 predstavitev. Na voljo so tudi videoposnetki in veliko zapisov v blogu, več kot 500. To je zelo obsežen vir. In če vas ta material zanima, vas vabim, da ga uporabite.

Preden sem začel delati s Postgresom, sem bil učitelj, profesor. In zelo sem vesel, da vam bom lahko sedaj predstavil to, kar bom predstavil. To je ena izmed mojih najbolj zanimivih predstavitev. Ta predstavitev vsebuje 110 diapozitivov. Začeli bomo z enostavnimi stvarmi, dokler ne bo na koncu predavanje postalo vse bolj zapleteno in zahtevno.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

To je precej neprijetna tema. Zaklepanje ni najbolj priljubljena tema. Želimo, da bi ta pojav izginil. Je kot obisk zobozdravnika.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

  1. Zaklepanje predstavlja problem za veliko ljudi, ki delajo z bazami podatkov in imajo več procesov, ki delujejo hkrati. Potrebujejo zaklepanje. Torej, danes vam bom dal osnovna znanja o zaklepanju.
  2. Identifikatorji transakcij. To je precej dolgočasen del predstavitve, vendar ga je treba razumeti.
  3. Nato se bomo pogovorili o vrstah zaklepanja. To je precej mehaničen del.
  4. In nato bomo navedli nekaj primerov zaklepanja. To bo precej zapleteno za razumevanje.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Pogovorimo se o zaklepanju.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Terminologija je pri nas precej zapletena. Koliko vas ve, od kod ta odlomek? Dva človeka. To je iz igre, ki se imenuje „Kolosalno pustolovščino v jami“. To je bila tekstovna računalniška igra v 80. letih, mislim. Tam si moral iti v jamo, v labirint, in tekst se je spreminjal, vendar je vsebina ostala približno enaka vsakokrat. Tako se spomnim te igre.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In tukaj vidimo imena blokad, ki so prišle k nam iz Oracle. Uporabljamo jih.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Tukaj vidimo izraze, ki me zmedo. Na primer, SHARE UPDATE EXCLUSIVE. Nato SHARE RAW EXCLUSIVE. Priznam, da ti izrazi niso zelo razumljivi. Poskusili bomo, da jih podrobno obravnavamo. Nekateri vsebujejo besedo „share“, kar pomeni – ločiti se. Nekateri vsebujejo besedo „exclusive“ — ekskluziven. V nekaterih sta prisotni obe besedi. Rad bi začel s tem, kako te blokade delujejo.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Prav tako je zelo pomembna beseda „dostop“ — access. In beseda „row“ — vrstica. Torej, razporeditev dostopa, razporeditev vrstic.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Še en problem, ki ga je treba razumeti v Postgresu, o katerem, na žalost, ne bom mogel govoriti v svojem nastopu, je MVCC. Imam posebno predstavitev o tej temi na svoji spletni strani. In če mislite, da je ta predstavitev zapletena, potem je MVCC verjetno moja najtežja tema. In če vas to zanima, lahko to pogledate na spletu. Poglejte lahko video.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Še ena stvar, ki jo moramo razumeti – to so identifikatorji transakcij. Mnoge transakcije ne morejo delovati brez edinstvenih identifikatorjev. In tukaj imamo razlago, kaj je transakcija. V Postgresu sta dve sistemi številčenja transakcij. Vem, da to ni zelo elegantna rešitev.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Prav tako imejte v mislih, da bodo diapozitivi precej zapleteno razumljivi, zato je treba pozornost posvetiti tistemu, kar je izpostavljeno v rdeči barvi.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

http://momjian.us/main/writings/pgsql/locking.sql

Poglejmo. Rdeče je označeno številko transakcije. Tukaj je prikazana funkcija SELECT pg_back. Vrne mojo transakcijo in ID te transakcije.

Eden trenutek, če vam je ta predstavitev všeč in jo želite zagnati v svoji bazi podatkov, lahko preprosto kliknete na to povezavo, označeno z roza barvo, in prenesete SQL za to predstavitev. Enostavno jo zaženite v vašem PSQL in celotna predstavitev se bo takoj prikazala na vašem zaslonu. Ne bo vsebovala barv, vendar jo bomo vsaj lahko videli.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

V tem primeru vidimo ID transakcije. To je številka, ki smo ji dodelili. Obstaja tudi drug tip ID transakcije v Postgres, ki se imenuje virtualni ID transakcije.

In to moramo razumeti. To je zelo pomembno, sicer ne bomo razumeli zaklepanja v Postgresu.

Virtualni ID transakcije je ID transakcije, ki ne vsebuje stalnih vrednosti. Na primer, če izvedem ukaz SELECT, verjetno ne bom spremenil baze podatkov, ne bom ničesar zaklenil. Zato, ko izvedemo preprost SELECT, ne dodelimo tej transakciji stalnega ID. Dodelimo ji samo virtualni ID.

To povečuje učinkovitost Postgres, izboljšuje možnosti za čiščenje, zato virtualni ID transakcije sestavljata dve številki. Prva številka pred poševnico je ID backend-a. Na desni strani vidimo preprosto števec.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Zato, ko izvedem poizvedbo, pove, da je ID backend-a - 2.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In če izvedem serijo takšnih transakcij, vidimo, da se števec vsakokrat povečuje, ko izvedem poizvedbo. Na primer, ko izvedem poizvedbo 2/10, 2/11, 2/12 itd.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Upoštevajte, da sta tukaj dve stolpci. Na levi strani vidimo virtualni ID transakcije - 2/12. Na desni strani pa imamo stalni ID transakcije. To polje je prazno. Ta transakcija ne modificira baze podatkov. Zato ji ne dodelim stalnega ID transakcije.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Takoj ko izvedem ukaz analizirati (ANALYZE), mi ista poizvedba vrne stalni ID transakcije. Poglejte, kako se je to spremenilo. Prej nisem imel tega ID-ja, zdaj pa je prišel.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Tukaj imamo še eno poizvedbo, še eno transakcijo. Virtualna številka transakcije je 2/13. In če zahtevam stalni ID transakcije, ga bom prejel, ko bom izvedel poizvedbo.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Torej, še enkrat. Imamo virtualni ID transakcije in stalni ID transakcije. Preprosto razumite ta trenutek, da boste lahko razumeli vedenje Postgres.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Prehajamo k tretjemu poglavju. Tukaj bomo le prešli skozi različne vrste zaklepov v Postgresu. To ni posebej zanimivo. Zadnji del bo veliko bolj zanimiv. Toda osnovne stvari moramo obravnavati, sicer ne bomo razumeli, kaj sledi.

Prešli bomo skozi ta razdelek, si ogledali vsako vrsto zaklepa. Pokazal vam bom primere, kako se postavijo, kako delujejo, in vam pokazal nekatere poizvedbe, ki jih lahko uporabite, da vidite, kako zaklepanje deluje v Postgresu.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Da bi ustvarili poizvedbo in videli, kaj se dogaja v Postgresu, moramo izdati poizvedbo v sistemskem pogledu. V tem primeru je naš pg_lock označen z rdečo. Pg_lock je sistemska tabela, ki nam pove, katere zaklepe trenutno uporablja Postgres.

Kljub temu mi je zelo težko pokazati pg_lock samega, ker je to kar zapleteno. Zato sem ustvaril pogled, ki prikazuje pg_locks. Prav tako opravlja nekaj dela zame, da bolje razumem. To pomeni, da izključuje moje zaklepe, mojo sejo itd. To je preprost SQL in bolje vam omogoča, da vidite, kaj se dogaja.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Še en problem je, da je ta pogled zelo širok, zato moram ustvariti drugega – lockview2.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian In prikazuje mi še dodatne stolpce iz tabele. In še enega, ki mi prikazuje ostale stolpce. To je kar zapleteno, zato sem se potrudil, da to predstavim čim bolj preprosto.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Torej, ustvarili smo tabelo, ki se imenuje Lockdemo. In v njej smo ustvarili en zapis. To je naša primerna tabela. Ustvarili bomo razdelke, da vam preprosto pokažemo primere zaklepov.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Torej, en zapis, en stolpec. Prvi tip zaklepa se imenuje ACCESS SHARE. To je najmanj omejujoč zaklep. To pomeni, da praktično ne konfliktira z ostalimi zaklepi.

In če želimo izrecno določiti zaklepanje, zaženemo ukaz "lock table". In ta očitno zaklene, tj. v načinu ACCESS SHARE zaženemo lock table. In če zaženem PSQL v ozadju, zaženem na ta način drugo sejo iz moje prve seje. Kaj torej tukaj naredim? Preklopim na drugo sejo in ji rečem "prikaži mi lockview za to poizvedbo". In tu imam AccessShareLock v tej tabeli. To je točno to, kar sem zahteval. In pravi, da je zaklepanje dodeljeno. Zelo preprosto.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Nato, če pogledamo v drugo kolono, tam ni nič. So prazne.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In če zaženem ukaz "SELECT", je to implicitna (jasna) metoda za pridobitev AccessShareLock. Zato sprostim svojo tabelo in zaženem poizvedbo, ki vrne več vrstic. In v eni od vrstic vidimo AccessShareLock. Na ta način SELECT sproži AccessShareLock v tabeli. In ta ne povzroča praktično nobenega konflikta, saj gre za nizkolegleno zaklepanje.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Kaj pa, če zaženem SELECT in imam tri različne tabele? Prej sem zagnal samo eno tabelo, zdaj pa zaženem tri: pg_class, pg_namespace in pg_attribute.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In zdaj, ko pogledam poizvedbo, vidim 9 AccessShareLocks v treh tabelah. Zakaj? S tremi tabelami, označenimi z blue: pg_attribute, pg_class, pg_namespace. Vendar pa lahko vidite, da vsi indeksi, določeni skozi te tabele, prav tako imajo AccessShareLock.

In to je zaklepanje, ki praktično ne povzroča konfliktov z drugimi. Vse, kar dela, je preprosto preprečuje, da bi lahko izpraznili tabelo, medtem ko jo izbiramo. To ima smisel. Tj. če izberemo tabelo in v tem trenutku izgine, potem je to narobe, zato. AccessShare – to je nizkolegleno zaklepanje, ki nam pove "ne brišite te tabele, dokler delam".. V bistvu, to je vse, kar počne.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

ROW SHARE – to zaklepanje je nekoliko drugačno.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Poglejmo primer. SELECT ROW SHARE je način zaklepanja vsake vrstice posebej.. Tako nihče ne more izbrisati ali spremeniti, medtem ko jih gledamo.

Odklepanje upravljalnika zaklepa Postgres. Bruce MomjianTorej, kaj dela SHARE LOCK? Vidimo, da je ID transakcije 681 za SELECT. In to je zanimivo. Kaj se je tukaj zgodilo? Prvič vidimo številko v polju „Lock“. Vzamemo ID transakcije, in pove, da jo blokira v ekskluzivnem načinu. Vse, kar počne, je, da pravi, da imam vrstico, ki je tehnično blokirana nekje v tabeli. Ampak ne pove, kje natančno. Malo kasneje bomo to podrobneje obravnavali.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Tukaj govorimo, da se blokada uporablja od nas.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Torej, ekskluzivna blokada izrecno (explicitly) pove, da je ekskluzivna. In tudi, če izbrišete vrstico v tej tabeli, se to tudi zgodi, kot lahko vidite.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

SHARE EXCLUSIVE – to je daljša blokada.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

To je (ANALYZE) ukaz analize, ki bo uporabljen.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

SHARE LOCK – lahko izrecno blokirate v načinu share.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Lahko tudi ustvarite edinstven indeks. In tam lahko vidite SHARE LOCK, ki je del tega. In blokira tabelo ter nanjo nastavi SHARE LOCK.

Privzeto SHARE LOCK na tabeli pomeni, da lahko drugi berejo tabelo, vendar je nihče ne more spreminjati. In prav to se zgodi, ko ustvarite edinstven indeks.

Če ustvarim edinstven concurrently indeks, bom imel drug tip blokade, ker, kot se spomnite, uporaba concurrently indeksov zmanjšuje zahteve po blokadi. In če uporabljam normalno blokado, normalni indeks, potem tako preprečim zapisovanje v indeks tabelo med njenim ustvarjanjem. Če uporabljam concurrently indeks, pa moram uporabiti drug tip blokade.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

SHARE ROW EXCLUSIVE – spet jo je mogoče določiti eksplicitno (explicitly).

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Ali lahko ustvarimo pravilo, tj. vzeti določeno situacijo, v kateri bo uporabljena.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

EXCLUSIVE blokada pomeni, da noben drug ne more spreminjati tabele.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Tukaj vidimo različne vrste blokad.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

ACCESS EXCLUSIVE, na primer, je ukaz blokade. Na primer, če delate CLUSTER table, to pomeni, da nihče ne more tja zapisovati. In blokira ne le samo tabelo, ampak tudi indekse.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

To je druga stran blokade ACCESS EXCLUSIVE, kjer vidimo natančno, kaj blokira v tabeli. Blokira posamezne vrstice tabele, kar je dovolj zanimivo.

To je vse osnovne informacije, ki sem jih hotel posredovati. Govorili smo o blokadah, ID transakcijah, govorili smo o virtualnih ID transakcijah, o stalnih ID transakcijah.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In zdaj se bomo podali skozi primere blokad. To je najzanimivejši del. Oglejali si bomo zelo zanimive primere. Moj cilj v tej predstavitvi je, da vam zagotovim boljšo predstavo o tem, kaj Postgres v resnici počne, ko poskuša blokirati določene stvari. Zdi se mi, da zelo dobro blokira posamezne dele.

Poglejmo nekatere specifične primere.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Začeli bomo s tabelami in eno vrstico v tabeli. Ko vstavim nekaj, se prikaže ExclusiveLock, ID transakcije in ExclusiveLock na tabeli.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Kaj pa, če vstavim še dve vrstici? Zdaj imamo v naši tabeli tri vrstice. Vstavil sem eno vrstico in dobil tole na izlazu. In če vstavim še dve vrstici, kaj je tukaj čudnega? Tuki je neobičajno, ker sem dodal tri vrstice v to tabelo, toda še vedno imam dve vrsti v tabeli blokad. In to je v bistvu temeljno vedenje Postgres.

Mnogi menijo, da če v bazi podatkov blokirate 100 vrstic, potem boste morali ustvariti 100 vnose blokad. Če bom blokiral hkrati 1 000 vrstic, potem bom potreboval 1 000 takih zahtev. In če moram blokirati milijon ali milijardo. Ampak, če bomo to počeli, ne bo delovalo dobro. Če ste uporabljali sistem, ki ustvarja vnose blokad za vsako posamezno vrstico, vidite, da je to zapleteno. Ker morate takoj določiti tabelo blokad, ki se lahko napolni, ampak Postgres tega ne počne.

In na tem diapozitivu je zelo pomembno, da je jasno prikazano, da obstaja še en sistem, ki deluje znotraj MVCC, ki blokira posamezne vrstice. Zato, ko blokirate milijarde vrstic, Postgres ne ustvari milijarde posameznih ukazov za blokado. In to zelo pozitivno vpliva na zmogljivost.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Kaj pa posodobitev? Sedaj posodabljam vrstico in lahko opazite, da je hkrati izvedlo dve različni operaciji. Hkrati je zaklenilo tabelo, vendar je tudi zaklenilo indeks. Indeks je bilo treba zakleniti zaradi edinstvenih omejitev v tej tabeli. Želimo se prepričati, da ga nihče ne spremeni, zato ga zaklenemo.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Kaj se zgodi, če želim posodobiti dve vrstici? In vidimo, da se obnaša enako. Izvajamo dvakrat več posodobitev, toda prav tako je enaka količina zaklenjenih vrstic.

Če vas zanima, kako to počne Postgres, bi se morali udeležiti mojih predstavitev o MVCC, da bi spoznali, kako Postgres notranje označuje te vrstice, ki jih spreminja. Postgres ima način, kako to doseže, vendar to ne deluje na nivoju zaklepanja tabel, temveč na bolj nizki in učinkoviti ravni.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Kaj pa, če želim nekaj izbrisati? Če izbrisem, na primer, eno vrstico in še vedno imam svoja dva vhoda za zaklep, in tudi če želim izbrisati vse, vendar ostajajo tam prisotni.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In, na primer, želim vnesti 1.000 vrstic, nato pa ali izbrisati ali dodati 1.000 vrstic, te posamezne vrstice, ki jih dodajam ali spreminjam, se ne zapisujejo tukaj. Zapisane so na bolj nizki ravni znotraj same vrstice. In med predstavitvijo o MVCC sem o tem govoril podrobno. Vendar je zelo pomembno, ko analizirate zaklepe, da se prepričate, da imate zaklep na nivoju tabel in da tukaj ne vidite, kako se zapisujejo posamezne vrstice.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Kaj pa eksplicitno zaklepanje?

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Če pritisnem 'posodobi', imam dve zaklenjeni vrstici. In če jih vse označim in pritisnem 'posodobi vse', mi še vedno ostajata dve zapisa zaklepa.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Ne ustvarjamo ločenih zapisov za vsako posamezno vrstico. Ker bi to zmanjšalo zmogljivost, lahko pride do prekomernega števila. In lahko se znajdemo v neprijetni situaciji.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In enako velja, če delamo shared, lahko to počnemo 30-krat.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Obnovimo svojo tabelo, vse izbrišemo, nato pa ponovno vstavimo eno vrstico.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Drugý typ správania, ktorý vidíte v Postgres, je veľmi dobre známe a žiaduce správanie – to, že môžete vykonávať update alebo select. A môžete to robiť súčasne. Select nezamkne update a naopak. Hovoríme, že čítajúci nezamkne toho, kto píše, a ten, kto píše, nezamkne čitateľa.

Ukážem vám príklad tohto. Teraz urobím výber. Potom vykonáme INSERT. A potom si budete môcť pozrieť – 694. Uvidíte ID transakcie, ktorá vykonala tento insert. A takto to funguje.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

A ak sa teraz pozriem na svoj backend ID, stalo sa – 695.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

A môžem vidieť, že 695 sa objavuje v mojej tabuľke.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

A ak vykonám aktualizáciu takto, dostanem iný prípad. V takom prípade je 695 výhradná blokácia, a update má rovnaké správanie, ale medzi nimi nevzniká konflikt, čo je dosť nezvyčajné.

A môžete si všimnúť, že hore je ShareLock a dole je ExclusiveLock. A obidve transakcie sa uskutočnili.

A treba si vypočuť moju prednášku o MVCC, aby ste pochopili, ako to prebieha. Ale toto je ilustrácia toho, že môžete robiť obidve veci súčasne, t.j. súčasne vykonávať SELECT a UPDATE.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Poďme resetovať a znova vykonať jednu operáciu.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Ak sa pokúsite spustiť súčasne dva updates na rovnakom riadku, zablokuje sa to. A pamätajte, hovoril som, že čítajúci nezamkne píšuceho, a píšuci nezamkne čítajúceho, ale jeden píšuci zablokuje druhého píšuceho. T.j. nemôžeme dovoliť, aby dvaja ľudia súčasne aktualizovali ten istý riadok. Musíme čakať, kým jeden z nich skončí.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

A aby som to ilustroval, pozriem sa na tabuľku Lockdemo. A pozrieme sa na jeden riadok. Na transakciu 698.

Aktualizovali sme to na 2. 699 – to je prvá aktualizácia. A tá prebehla úspešne, alebo sa nachádza vo vyčkávajúcej transakcii a čaká, kým potvrdíme alebo zrušíme.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Vendar poglejte še na drugo – 2/51 – to je naša prva transakcija, naša prva seja. 3/112 – to je drugi zahtevek, ki se je prikazal zgoraj in spremenil to vrednost na 3. In če opazite, je zgornji blokiral samega sebe, tistega 699. Toda 3/112 ni zagotovil zaklepanja. V stolpcu Lock_mode piše, da čaka. Čaka na 699. In če pogledate, kje je 699, je višje. In kaj je prva seja naredila? Ustvarila je ekskluzivno zaklepanje na svojem lastnem ID-ju transakcije. Tako to dela Postgres. Blokira lastni ID transakcije. In če želite čakati, da nekdo potrdi ali prekliče, morate čakati, dokler je čakanje na transakcijo. In zato lahko vidimo čuden vrstic.

Poglejmo še enkrat. Na levi vidimo naše obdelovalno ID. V drugem stolpcu vidimo naš virtualni ID transakcije, v tretjem pa vidimo lock_type. Kaj to pomeni? V bistvu pravi, da blokira ID transakcije. Toda opazite, da je v vseh vrsticah spodaj napisano relation. In zato imate dva tipa zaklepanja v tabeli. Obstaja zaklepanje relation. Obstaja tudi zaklepanje transactionid, kjer blokirate sami, točno to se dogaja v prvi vrstici ali na dnu, kjer transationid, kjer čakamo, da 699 zaključi svojo operacijo.

Gledam, kaj se tukaj dogaja. In tukaj se hkrati dogajata dve stvari. Poglejte zaklepanje po ID-ju transakcije v prvi vrstici, ki blokira samo sebe. In blokira samo sebe, da prisili ljudi, da čakajo.

Če pogledate 6. vrstico, je to ista zapis, kot prva. In zato je transakcija 699 blokirana. 700 se tudi sam blokira. In potem v spodnji vrstici boste videli, da čakamo, da 699 zaključi svojo operacijo.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In v lock_type, tuple vidite številke.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Lahko vidite, da je to 0/10. In to je številka strani, in tudi offset tega konkretnega vrstica.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In vidite, da postane 0/11, ko posodobimo.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Toda v resnici – to je 0/10, ker se dogaja čakanje na to operacijo. Imamo priložnost pogledati, da je to tisti vrstica, ki jo čakam, da potrdim.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Ko se to potrdi in pritisne commit, in ko se posodobitev konča, je to, kar dobimo spet. Transakcija 700 je edina zaklenitev, ne čaka več nikogar, ker je bila odobrena. Čaka samo, da se transakcija konča. Takoj ko se 699 konča, ne čakamo več na nič. In zdaj transakcija 700 pravi, da je vse v redu, da ima vse zaklenitve, ki jih potrebuje, v vseh dovoljenih tabelah.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In da bi še dodatno zapletli stvari, ustvarimo še en pogled, ki nam tokrat ponuja hierarhijo. Ne pričakujem, da boste razumeli to poizvedbo. Ampak to nam bo dalo jasnejši vpogled v to, kar se dogaja.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

To je rekurzivni pogled, ki ima še eno sekcijo. In potem znova vrne vse skupaj. Uporabimo to.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Kaj če izvedemo tri sočasne posodobitve in rečemo, da je vrstica zdaj enaka trem. In zamenjamo 3 z 4.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In tukaj vidimo 4. In ID transakcije 702.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Nato spremenim 4 v 5. In 5 v 6, ter 6 v 7. In formiram vrsto ljudi, ki bodo čakali, da se ta ena transakcija konča.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In vse postane jasno. Katera je prva vrstica? To je 702. To je ID transakcije, ki je prvotno določil to vrednost. Kaj pa je napisano v stolpcu Granted? Imam oznake. f. To so moje posodobitve, ki (5, 6, 7) ne morejo biti odobrene, ker čakamo, da se transakcijski ID 702 konča. Imamo tam zaklenitev transakcijskega ID. In rezultat je 5 zaklenitev transakcijskih ID.

In če pogledate 704, 705, tam še ni nič napisanega, ker še ne vedo, kaj se dogaja. Preprosto pišejo, da nimajo pojma, kaj se dogaja. In zgolj gredo v spanec, ker čakajo, da nekdo konča in jih zbudi, ko se pojavi priložnost zamenjati vrstico.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

To je tako, kot izgleda. Jasno je, da vsi čakajo na 12. vrstico.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

To je tisto, kar smo videli tukaj. Tukaj je 0/12.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Tako da, ko je prva transakcija odobrena, lahko tukaj vidite, kako deluje hierarhija. In zdaj postane vse jasno. Vse postanejo čiste. In pravzaprav so še vedno v pričakovanju.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Tako se dogaja. 702 se zaključi. Zdaj 703 pridobi to blokado vrstice, nato 704 čaka, da se 703 zaključi. In 705 tudi to čaka. Ko se vse to zaključi, se očistijo sami. Rad bi opozoril, da se vsi postavijo v vrsto. To je zelo podobno prometni situaciji, kjer vsi čakajo na prvi avto. Prvi avto se ustavi, in vsi se formirajo v dolgo vrstico. Potem se premakne, nato lahko naslednji avto nadaljuje naprej in pridobi svojo blokado itd.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In če se vam to zdi preprosto, bomo zdaj govorili o mrtvih blokadah. Ne vem, kdo izmed vas se je s tem že srečal. To je precej pogost problem v sistemih baze podatkov. Mrtve blokade so tisti primer, ko ena seja čaka, da nekaj izvede druga seja. In v tem trenutku druga seja čaka, da prva seja izvede nekaj.

In na primer, če Janez reče: 'Daj mi nekaj', jaz pa rečem: 'Ne, to ti dam le, če mi daš nekaj drugega.' On pa reče: 'Ne, ne bom ti dal, dokler mi ne daš.' In tako se znajdemo v situaciji mrtve blokade. Prepričan sem, da Janez ne bi tako ravnal, ampak razumete bistvo: imamo dva človeka, ki želita nekaj pridobiti, vendar nista pripravljena dati, dokler jima drugi ne da tistega, kar želita. In tukaj ni rešitve.

V bistvu mora vaša baza podatkov to odkriti. Nato je treba eno izmed sej odstraniti ali zapreti, ker sicer bodo tam ostale za vedno. To vidimo v bazah podatkov, to vidimo v operacijskih sistemih. In povsod, kjer imamo vzporedne procese, se to lahko zgodi.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Zdaj bomo postavili dve mrtvi blokadi. Postavili bomo 50 in 80. V prvi vrsti bom izvedel posodobitev z 50 na 50. Dobim številko transakcije 710.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Nato bom spremenil 80 v 81 in 50 v 51.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In tako bo to izgledalo. Zato ima 710 blokado vrstice, 711 pa čaka na potrditev. To smo videli, ko smo posodabljali. 710 je lastnik naše vrstice. 711 čaka, da 710 zaključi transakcijo.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Tudi navedeno je, na kateri vrstici se dogajajo mrtve blokade. In tukaj se začne stvari skrivati.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Zdaj posodabljamo 80 na 80.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In this is where deadlocks begin. 710 is waiting for a response from 711, and 711 is waiting for 710. This will not end well. There is no way out of this. They will be waiting for each other’s responses.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

And this will just start causing delays. We don’t want that.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Postgres has ways to detect when this happens. When it happens, you will receive such an error. It becomes clear that a process is waiting for a SHARE LOCK from another process, which is blocked by process 711. That process was waiting for a SHARE LOCK for a certain transaction ID and was blocked by another process. So we have a deadlock situation here.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Are there three-way deadlocks? Is this possible? Yes.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

We input these numbers into the table. We change 40 to 40, we make a lock.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

We change 60 to 61, 80 to 81.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Then we change 80, and then – boom!

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Now 714 is waiting for 715. 716 is waiting for 715. And there’s nothing that can be done about it.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Here we have not two people but three. I want something from you, this one wants something from the third person, and the third person wants something from me. We end up in a three-way wait because we’re all waiting for the other to finish what they need to do.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Postgres knows which row this is happening on. So it will give you the following message indicating that you have a problem where three entities are blocking each other. There are no constraints here. This can happen where 20 entries are blocking each other.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

The next problem is serializable.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

If there is a special serializable lock.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

And we return to 719. It has a completely normal output.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

And you can click to make a transaction serializable.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

You understand that you now have a different type of lock SA – it means serializable.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

And therefore we have a new type of lock called SARieadLock, which is a serial lock allowing to introduce serial numbers.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

And you can also insert unique indexes.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In this table we have unique indexes.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

So if I enter number 2 here, I have 2. But at the very top I insert another 2. And you can see that 721 has an exclusive lock. But now 722 is waiting for 721 to finish its operation because it cannot insert 2 until it knows what will happen with 721.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

And if we do a subtransaction.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Here we have 723.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In če obdržimo točko in jo nato posodobimo, dobimo nov transakcijski ID. To je še en način obnašanja, ki ga morate poznati. Če to vrnemo, gre transakcijski ID. 724 se izgubi. Zdaj pa dobimo 725.

In kaj poskušam narediti tukaj? Poskušam vam pokazati primere nenavadnih blokad, ki jih lahko najdete: bodisi serializable blokade bodisi SAVEPOINT – to so različne vrste blokad, ki se bodo pojavile v tabeli blokad.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

To je ustvarjanje eksplicitnih (jasnih) blokad, ki imajo pg_advisory_lock.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In vidite, da je tip blokade tukaj naveden kot advisory. In tu je rdeče napisano 'advisory'. In hkrati lahko tako zaklenete s pg_advisory_unlock.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In za konec bi vam rad pokazal še eno fascinantno stvar. Ustvaril bom še eno vrsto. Ampak bomo povezali tabelo pg_locks s tabelo pg_stat_activity. In zakaj to želim narediti? Ker mi bo to omogočilo, da si ogledam in vidim vse trenutne seje ter katere blokade dejansko pričakujejo. To je precej zanimivo, ko združimo tabelo blokad in tabelo poizvedb.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In tukaj ustvarjamo pg_stat_view.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In posodobimo vrstico za eno. In tukaj vidimo 724. Nato zmanjšamo našo vrstico na tri. In kaj vidite tukaj zdaj? To so poizvedbe, tj. vidite celoten seznam poizvedb, ki so navedene v levem stolpcu. Na desni strani pa lahko vidite blokade in tisto, kar ustvarjajo. In to je lahko bolj razumljivo za vas, da vam ni treba vsakič vrniti se k vsaki seji in preverjati – ali se ji pridružiti ali ne. To opravi namesto vas.

Še ena funkcija, ki je zelo uporabna – je pg_blocking_pidsMorda niste nikoli slišali o tem. Kaj počne? Omogoča nam, da rečemo, kateri natančni ID-ji procesov čaka za to sejo 11740. In lahko vidite, da 11740 čaka na 724. In 724 je na vrhu. 11306 je vaš ID-proces. V bistvu ta funkcija preiskuje vašo blokirno tabelo. In vem, da je to malo zapleteno, ampak uspete to razumeti. Ta funkcija prehaja skozi to blokirno tabelo in skuša najti, kje je ta ID procesa, ob upoštevanju blokad, ki jih čaka. Prav tako poskuša izračunati, kateri ID procesa ima tisti proces, ki čaka na blokade. Zato lahko to funkcijo zaženete. pg_blocking_pids.

In to je zelo koristno. Dodali smo to šele od različice 9.6, zato ta funkcija obstaja le 5 let, a je zelo in zelo uporabna. Enako velja za drugi poizvedbo. Pokaže natanko tisto, kar potrebujemo videti.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

To je tisto, o čemer sem želel govoriti z vami. In kot sem pričakoval, smo porabili vse naše čas, ker je bilo toliko diapozitivov. Diapozitivi so na voljo za prenos. Rad bi se vam zahvalil, da ste bili tukaj. Prepričan sem, da vam bo ostanek konference všeč, najlepša hvala!

Vprašanja:

Na primer, če poskušam posodobiti vrstice, medtem ko druga seja poskuša izbrisati celo tabelo. Kolikor razumem, bi moral biti tam kaj takega kot namera zaklep. Ali kaj takega obstaja v Postgresu?

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

Vrnimo se na začetek. Morda se spomnite, da kadar koli nekaj počnete, na primer, kadar izvajate SELECT, izdajamo AccessShareLock. In to preprečuje brisanje tabele. Torej, če na primer želite posodobiti vrstico v tabeli ali izbrisati vrstico, nekdo ne more istočasno izbrisati cele tabele, ker obdržite ta AccessShareLock nad celo tabelo in nad vrstico. In ko končate, lahko to izbrišejo. Ampak dokler neposredno spreminjate kaj, ne morejo tega storiti.

Poglejmo še enkrat. Preverimo primer z brisanjem. In vidite, da obstaja ekskluzivni zaklep nad celo tabelo.

To bi izgledalo kot ekskluzivni zaklep, kajne?

Da, to izgleda kao to. Razumem o čemu govorite. Kažete da, ako izvršim SELECT, dobiću ShareExclusive, a zatim to prebacim u stanje Row Exclusive, postaje li to problem? Ali iznenađujuće, to ne stvara problem. To je kao da se povećava nivo zaključavanja, ali suštinski imam zaključavanje koje sprečava brisanje. I sada, kada ovo zaključavanje učinim jačim, ono i dalje sprečava brisanje. Zato nije da se penjem. T. e. sprečavao je to čak i kada je bio na nižem nivou, tako da, kada povećam njegov nivo, on i dalje sprečava brisanje tabele.

Razumem o čemu govorite. Ovdje nema slučaja povećanja nivoa zaključavanja, gde pokušavate da se rešite jednog zaključavanja kako biste uneli jače. Ovdje se jednostavno svuda povećava to sprečavanje, tako da ne izaziva nikakav sukob. Ali to je dobro pitanje. Hvala vam puno što ste ga postavili!

Šta treba da uradimo kako bismo izbegli situaciju deadlock-a kada imamo mnogo sesija, puno korisnika?

Postgres automatski primećuje situacije deadlock-a. I automatski će ukloniti jednu od sesija. Jedini način da se izbegne situacija mrtvog zaključavanja je da blokirate ljude u istom redu. Dakle, kada pogledate vašu aplikaciju, često je uzrok deadlock-a… Zamislimo da želim da blokiram dve različite stvari. Jedna aplikacija blokira tabelu 1, a druga aplikacija blokira 2, a potom tabelu 1. Najjednostavniji način da izbegnete deadlock-e je da pogledate vašu aplikaciju i pokušate da se uverite da se zaključavanje dešava u istom redosledu u svim aplikacijama. I to obično uklanja 80% problema, jer razni ljudi pišu te aplikacije. I ako ih blokirate u istom redu, ne susrećete se sa situacijom deadlock-a.

Hvala vam mnogo za vaše izlaganje! Govorili ste o vacuum full i, ako pravilno razumem, vacuum full deformiše redosled zapisa u posebnom skladištu, tako da zadržava trenutne zapise nepromenjenim. A zašto vacuum full zahteva ekskluzivnu blokadu i zašto sukobljava sa operacijama pisanja?

To je dobro vprašanje. Razlog je, da vacuum full zajame tabelo. In dejansko ustvarjamo novo različico tabele. In tabela bo nova. Tako da bo to popolnoma nova različica tabele. Težava je v tem, da ne želimo, da ljudje berejo to, ker moramo omogočiti, da vidijo novo tabelo. In tako se povezuje s prejšnjim vprašanjem. Če bi lahko brali hkrati, ne bi mogli premikati in usmerjati ljudi na novo tabelo. Čakati bi morali, da vsakdo konča z branjem te tabele, zato gre dejansko za situacijo ekskluzivne zaklepanja.
Samo rečemo, da zaklepamo od začetka, ker vemo, da bomo na koncu potrebovali ekskluzivno zaklepanje, da premaknemo vse na novo kopijo. Zato to potencialno lahko razrešimo. In tako to počnemo s sočasnim indeksiranjem. A to je precej bolj zapleteno. In to se zelo nanaša na vaše prejšnje vprašanje o ekskluzivnem zaklepanju.

Ali je mogoče dodati timeout zaklepanja v PostgreSQL? V Oraclu lahko, na primer, napišem 'izberi za posodobitev' in čakam 50 sekund na posodobitev. To je bilo dobro za aplikacijo. Toda v PostgreSQL moram to storiti takoj in ne čakati, ali pa čakati do nekega časa.

Da, lahko izberete timeout za vaše zaklepe. Prav tako lahko izdate ukaz no way, ki bo …, če zaklepa ne morete takoj dobiti. Torej, bodisi timeout zaklepanja, bodisi nekaj drugega, kar vam to omogoča. To se ne izvaja na sintaktični ravni. To je izvedeno kot spremenljivka na strežniku. Včasih tega ni mogoče uporabiti.

Lahko odprete 75. diapozitiv?

Da.

Odklepanje upravljalnika zaklepa Postgres. Bruce Momjian

In moje vprašanje je naslednje. Zakaj oba postopka posodobitve čakajo na 703?

In questo caso, è un'ottima domanda. Non capisco, tra l'altro, perché Postgres faccia così. Ma quando è stato creato il 703, si aspettava il 702. E quando appaiono i 704 e 705, sembra che non sappiano cosa aspettarsi, perché non c'è ancora nulla. E Postgres fa così: quando non puoi ottenere un lock, scrive 'Qual è il senso di elaborarti?', perché stai già aspettando qualcun altro. Quindi lo lasciamo semplicemente in sospeso, non lo aggiorna affatto. Ma cosa è successo qui? Non appena il 702 ha completato il processo e il 703 ha ottenuto il suo lock, il sistema è tornato indietro. E ha detto che ora abbiamo due persone in attesa. E poi aggiorniamoli insieme. E indichiamo che entrambi sono in attesa.

Non so perché Postgres faccia così. Ma c'è un problema che si chiama f…. Mi sembra che questo non sia un termine in italiano. È quando tutti aspettano un lock, anche se ci sono 20 istanze che aspettano il lock. E all'improvviso si svegliano tutti contemporaneamente. E tutti iniziano a cercare di reagire. Ma il sistema fa in modo che tutti aspettino il 703. Perché stanno tutti aspettando, e li mettiamo subito in fila. E se appare qualsiasi altra nuova richiesta, che è stata formata dopo, ad esempio, 707, ci sarà di nuovo un vuoto.

E mi sembra che ciò sia fatto affinché si possa dire che a questo punto il 702 sta aspettando il 703, mentre tutti coloro che arriveranno dopo non avranno alcuna registrazione in questo campo. Ma non appena il primo in attesa se ne va, e tutti coloro che stavano aspettando in quel momento prima dell'aggiornamento ricevono lo stesso segnaposto. Ecco perché credo che sia fatto in modo che possiamo elaborare in ordine, affinché siano correttamente ordinati.

Ho sempre visto questo come un fenomeno piuttosto strano. Perché qui, ad esempio, non li elenchiamo affatto. Ma mi sembra che ogni volta che diamo un nuovo lock, guardiamo tutti coloro che sono in attesa. Così li mettiamo tutti in fila. E poi chiunque arrivi, entra in coda solo quando la prossima persona ha finito di elaborarsi. Domanda molto interessante. Grazie mille per la domanda!

Mi sembra molto più logico quando il 705 aspetta il 704.

Toda težava je naslednja. Tehnično lahko zbudimo enega ali drugega. In zato bomo zbudili tistega ali drugega. Kaj pa se zgodi v delovanju sistema? Vidite, kako je 703 na vrhu blokiral svoj lasten ID transakcije. Tako deluje Postgres. In 703 se blokira z lastnim ID-jem transakcije, zato, če želi nekdo čakati, bo čakal na 703. In, v bistvu, 703 zaključi. Šele po njegovi dokončanju se kateri koli od procesov zbudi. In ne vemo, kateri proces bo to. Potem postopoma vse obdelujemo. Ni pa jasno, kateri proces se najprej zbudi, saj je lahko kateri koli izmed teh procesov. V bistvu smo imeli načrtovalec, ki je rekel, da zdaj lahko zbudimo kateri koli izmed teh procesov. Enostavno izberemo enega naključno. Zato oba morata biti označena, ker lahko zbudimo katerega koli od njiju.

In težava je v tem, da imamo CP-neskončnost. In zato je zelo verjetno, da bomo zbudili kasnejšega. In če, na primer, zbudimo kasnejšega, potem bomo pričakovali tistega, ki je ravno prejel blokado, zato ne določamo, kdo bo prvi zbudil. Ustvarjamo preprosto tako situacijo, sistem pa jih bo budil v naključnem vrstnem redu.

Je članki o locks Egora Rogova. Poglejte, tudi ti so zanimivi in koristni. Tema je seveda zelo zapletena. Hvala lepa, Bruce!

Izvor: habr.com

Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki 🔥 Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki | ProHoster