Image
18.5.2016 0 Comments

Databázy III / 3.časť

Späť na úvod >> Späť na seriál

Leto je v plnom prúde, mnohí z nás chodia k vode. Aj ja si chodím zaplávať medzi pirane na naše miestne plážovisko, pomenované podľa jednej farby vody (to aby som nerobil reklamu). A môj bicyklík som si zamykal zámkom. Ale jedného dňa sa mi zámok stratil, a tak je veru veľmi zle, keď si nemám čím svoj bicykel zamknúť. Jednoducho bez zámkov to v dnešnom svete nejde. Ale akú to má súvislosť s databázami? Aj tu sa používajú zámky! Neveríte?
VLÁKNO. Doteraz sme tvorili aplikácie, ku ktorým pristupovali jednotliví klienti akosi sporadicky, teda nie súčasne. Ale v praxi sa stáva pravý opak.
Predstavme si situáciu, že sme vlastníkmi centrálneho obchodu s elektronikou a vlastníme (okrem bavoráka, chichichi) aj účtovný systém na báze SQL, cez ktorý si jednotliví obchodníci objednávajú u nás tovar. Je samozrejmé, že každý obchodník sídli v inej časti krajiny a do nášho systému sa pripája – akože inak – z diaľky.
Keďže sme veľmi prosperujúci veľkosklad, naša databáza je riadne vyťažená. A to tak, že sa často stáva, že klienti posielajú svoje požiadavky skoro súčasne. Zatiaľ čo sa jedna požiadavka snaží dáta z databáze čítať, iná sa pokúša zapisovať a ďalšie na túto možnosť len čakajú.

Každá požiadavka prichádzajúca do MySQL servera od iného klienta prichádza akoby po zvláštnom kanáli, ktorý si môžeme predstaviť ako nejaký drôt alebo vlákno. V odbornej terminológii sa tomuto kanálu skutočne hovorí vlákno (angl. thread). Vlákno vznikne pri nadviazaní spojenia a po ukončení požiadavky automaticky zanikne. MySQL server môže obsluhovať naraz niekoľko vlákien, dokonca aj vo vzťahu k jednej tabuľke. Všetky vlákna sa správajú tak, aby (pokiaľ možno) svojou činnosťou nenarušili činnosť iných vlákien.

Poznámka:
Aj keď sa zdá, že podstata vlákien je podobná procesom v operačnom systéme, rozdiel tu je! Služba démon MySQL je sama osebe jedným z procesov bežiacich v OS, ktorý môže obsahovať naraz mnoho vlákien.

PRÍKLAD Č. 1. Nech v našom účtovnom systéme máme tabuľku Zasoby, obsahujúcu informácie o stave zásob. Tabuľka obsahuje položku sklad_pocet, kde sú výšky zásob uložené.
Nech v danom okamihu obchodník č. 1 z východnej časti krajiny pomocou klientskej aplikácie objedná určité množstvo – pre tento prípad 1 ks – televízneho prijímača.
Serverová aplikácia zistí, či sa na sklade nachádza dostatočné množstvo vyžiadaného tovaru. Objednávka je prijatá  za predpokladu, že stav zásob je vyšší alebo rovný požiadavke obchodníka č. 1. Na záver sa zníži stav zásob o objednaný počet (teda v tomto prípade o jeden kus).
Naznačme si sekvenciu príkazov SQL, ktoré budú jadrom aplikácie (premenné sú len ilustratívne):

SELECT sklad_pocet FROM zasoby WHERE id_tovaru = id_objed_tovaru

# kód, ktorý overí dostatok tovaru na sklade
# ak áno, potom prijme objednávku a aktualizuje počet zásob...

UPDATE Zasoby SET sklad_pocet = mnozstvo_zo_selectu - mnozstvo_obj_tovaru
      WHERE id_tovaru = id_objed_tovaru

Medzitým, čo sa tieto riadky vykonávajú, objednáva rovnaký tovar úplne iný obchodník č. 2 zo západnej časti krajiny. Rovnaký blok kódu sa začne vykonávať, ale už na inom vlákne.
Čo sa stane?
V tomto prípade si obe vlákna vzájomne narušia výpočty. Nesprávny predpoklad niektorého z vlákien môže mať za následok zápis chybného údaja do tabuľky. Pozrime sa na obrázok č. 1. Ak bude náš systém fungovať takto, pravdepodobne obchodníka č. 2 sklameme. Prečo? Obchodník č. 1 si v okamihu A objedná 1 ks televízora. Aplikácia sa v okamihu C pozrie (príkazom SELECT) do skladových zásob a zistí, že v sklade sú ešte dva televízory. V okamihu E na základe výšky zásob (keďže požiadavka je iba na jeden, tak je objednávka prijatá) začne objednávku spracúvať a aplikácia (príkazom UPDATE) upraví výšku zásob na zostávajúci jeden kus v okamihu G.
Skoro súčasne, ale predsa o chvíľočku neskôr obchodník č. 2  v časovom úseku B objedná 2 ks televízorov. Aplikácia (na inom vlákne!) v okamihu D zo zásob zistí, je na sklade sú ešte 2 televízne prijímače (počet 2 tam je preto, lebo prvé vlákno ešte neodpísalo 1 ks TV, objednaný obchodníkom č. 1. To urobí až nasledujúci krok – v okamihu E!). Takže toto vlákno stále počíta z dvoma kusmi TV! V okamihu F vlákno zistí, že pre objednávku č. 2 je to stále dosť, takže ju začne spracúvať a v okamihu H odpočíta od výsledku príkazu SELECT 2 ks a príkazom UPDATE zapíše, že na sklade zostáva 0 (2 – 2) kusov TV. Aj keď bude objednávka obchodníka č. 2 prijatá, v skutočnosti zostane nesplnená, lebo my fyzicky nemáme 3 televízne prijímače.
Logicky vieme, že to nie je pravda. Veď predsa na začiatku obidvoch operácií boli v sklade len 2 ks. Obchodník č. 1 si objednal jeden kus a obchodník č. 2 dva kusy, takže nemôžu byť obidvaja uspokojení a zároveň v sklade zostať nula kusov.
Kde nastala chyba?
Práve medzi úsekmi DG. V tom čase druhé vlákno príkazom SELECT načítalo stav zásob, ktorý nebol ešte upravený – aktualizovaný prvým vláknom príkazom UPDATE.

PRÍKLAD Č. 2. Riešením tohto problému sú zámky. Nie také bicyklové, ale programové. Zamykanie tabuliek poskytuje vláknam výhradný prístup k zdrojovým tabuľkám. Výhradný prístup znamená, že záznam, ktorý práve upravujeme, nemôže zmeniť žiadny iný používateľ. Po dokončení úprav je zámok uvoľnený a prístup k záznamu môžu získať ostatné vlákna iných používateľov.

Poznámka:
Typy zamykania v rôznych systémoch SQL môžu byť rôzne. Spravidla sa nemusí zamykať celá tabuľka, ale sa zamkne iba požadovaný riadok v tabuľke, ba dokonca iba jedno políčko v ňom. MySQL dokáže zamykať iba celú tabuľku naraz. Mnohí ne-maj-es-kvé-el-áci to tejto databáze vyčítajú ako nedostatok. Ale vzhľadom na jej nepomernú rýchlosť sa to v praxi neprejaví ako veľké zdržanie.

V MySQL sa tabuľka zamyká príkazom
LOCK TABLES meno_tabuľky (AS alias) {READ | [LOW_PRIORITY] | WRITE}

Príkaz LOCK TABLES je možné použiť na viac tabuliek naraz, kde sa jednotlivé zápisy oddeľujú čiarkou. V MySQL existujú dva druhy zámkov – zámok pre čítanie (READ) a zámok pre zápis (WRITE). Pri práci s týmito zámkami platia tieto nasledujúce zásady:

  • Ak niektoré vlákno zamkne tabuľku pre čítanie (zámok typu READ), môže z nej toto vlákno, ako aj všetky ostatné vlákna iba čítať.
  • Ak niektoré vlákno zamkne tabuľku pre zápis (zámok typu WRITE), získa k nej výhradný prístup, teda len toto vlákno môže z danej tabuľky čítať alebo do nej zapisovať. Ostatné vlákna musia čakať (nemôžu ani čítať ani zapisovať), pokým toto vlákno zámky neodstráni.

Odomknutie tabuľky sa vykoná príkazom UNLOCK TABLES. Prepracujme výpis z prvého príkladu tak, aby sme využili zámky:

LOCK TABLES Zasoby WRITE

SELECT sklad_pocet FROM Zasoby WHERE id_tovaru = id_objed_tovaru

# kód, ktorý overí dostatok tovaru na sklade
# ak áno, potom prijme objednávku a aktualizuje počet zásob...

UPDATE Zasoby SET sklad_pocet = mnozstvo_zo_selectu - mnozstvo_obj_tovaru
      WHERE id_tovaru = id_objed_tovaru

UNLOCK TABLES

Teraz bude aktualizácia záznamov v tabuľke určitým spôsobom riadená, lebo vlákno obchodníka č. 2 musí čakať, pokiaľ prvé vlákno nedokončí začaté zmeny. Pozrime sa na obrázok č. 2.
Vlákno obchodníka č. 1 je podobné ako v predchádzajúcom príklade, len v okamihu C pribudol zámok na tabuľku Zasoby.
Keď však obchodník č. 2 v okamihu B vyšle objednávku na 2 kusy televízneho prijímača, druhé vlákno, ktoré má spracúvať túto úlohu, musí čakať, pokiaľ sa neskončí akcia prvého vlákna, teda pokiaľ prvé vlákno nezaktualizuje stav zásob na 1 kus a neodomkne príslušnú tabuľku. To sa stane až v okamihu G.

Až v tomto momente začne pracovať druhé vlákno a v okamihu H pre zmenu ono zamkne tabuľku pre seba. V nasledujúcom okamihu však zistí, že zákazník síce požaduje 2 kusy televízorov, ale na sklade je iba 1 kus, čo nestačí! Objednávku nevyrieši, ale upozorní obchodníka č. 2, že jeho objednávka nemôže byť uspokojená pre nedostatok tovaru. Pekné, nie?
PRÍKLAD Č. 3. V predchádzajúcich príkladoch sme používali iba jednu tabuľku – Zasoby. Dopyty – queries – však bežne pracujú s viacerými tabuľkami. Postupnosť dopytov pri jednej operácii sa môže niekedy skladať aj z niekoľkých krokov. V takom prípade musíme pred začatím úprav zamknúť všetky príslušné tabuľky.
Predstavme si, že náš kód je už prepracovanejší a chceme do tabuľky obchodných transakcií s názvom Objednavky pridať záznam o práve prebiehajúcej objednávke. Ako nový záznam tabuľky Objednavky budeme pridávať meno a adresu zákazníka.
Postupnosť príkazov SQL by mohla byť potom takáto:

LOCK TABLES Zasoby WRITE, Objednavky WRITE, Zakaznici READ

SELECT sklad_pocet FROM Zasoby WHERE id_tovaru = id_objed_tovaru

# kód, ktorý overí dostatok tovaru na sklade
# ak áno, potom príjme objednávku a aktualizuje počet zásob...

UPDATE Zasoby SET sklad_pocet = mnozstvo_zo_selectu - mnozstvo_obj_tovaru
      WHERE id_tovaru = id_objed_tovaru

SELECT meno, adresa FROM Zakaznici WHERE id_zakaznika = id_objednavajuceho_zakaznika

# vloženie získaných údajov do premenných meno_z a adresa_z

INSERT INTO Objednavky VALUES (meno_z, adresa_z)

UNLOCK TABLES

Všimnime si, že sme tabuľky Zasoby a Objednavky uzamkli pre zápis, zatiaľ čo tabuľku Zakaznici sme zamkli iba pre čítanie.
Prečo?
Túto tabuľku (Zakaznici) nemusíme zamykať pre výhradný prístup, pretože do nej nebudeme nič zapisovať. Ostatné vlákna budú môcť z nej čítať, aj keď sme ju uzamkli.
Zapamätajme si!
Ak z tabuľky iba čítame (príkaz SELECT), môžeme ju zamknúť iba pre zápis (READ).
Pre ostatné príkazy SQL (INSERT, UPDATE, DELETE) musíme použiť zámok pre zápis (WRITE).

UVIAZNUTIE. Uvedomme si, že všetky tabuľky, s ktorými v našom projekte pracujeme, musia byť uzamknuté od počiatku úprav až do ich konca. Zabráni to uviaznutiu alebo zablokovaniu, čo je situácia, pri ktorej sú trvale zablokované dve či viac vlákien a každé vlákno čaká na tabuľku, ktorá je výlučne držaná iným zablokovaným vláknom. Keby sme neuzamkli všetky príslušné tabuľky, mohli by všetky aktívne vlákna začať operáciu, ale žiadne by ju nemohlo dokončiť. Predpokladajme situáciu, keď vlákno č. 1 zamkne tabuľku A a vlákno č. 2 zamkne tabuľku B. Vlákno č. 2 však potrebuje na dokončenie operácie údaje z tabuľky A (ktorú si uzamklo vlákno č. 1). Musí teda čakať, pokým ju vlákno č. 1 neuvoľní. K tomu však nemusí dôjsť. Môže sa totiž stať, že práve vlákno č. 1 tiež čaká na uvoľnenie tabuľky B. Tejto situácii sa hovorí uviaznutie a v odbornej angličtine sa uvádza ako deadlock (smrteľný zámok).

FRONTY. Zadanie príkazu LOCK TABLES v požiadavke na uzamknutie ešte neznamená, že sa uzamknutie podarí! Pred uzamknutím tabuľky pre výhradný alebo zdieľaný prístup musí najprv prebehnúť na MySQL serveri zložitý proces zaradenia do frontu požiadaviek. Na uzamknutie tabuľky musí vlákno spravidla určitý času počkať (ide len o zlomky sekúnd!!!).
Mechanizmus radenia do frontov existuje preto, aby požiadavky na uzamknutie tabuľky mohli túto určitú dobu počkať, pokým iné vlákna požadované tabuľky neuvoľnia. Pre zámky typu READ a WRITE existujú dva samostatné fronty, ktoré existujú na odlišných princípoch.

Pre pokus o uzamknutie tabuľky s výhradným prístupom WRITE platí nasledujúci postup:

  • Ak nie je ešte požadovaná tabuľka zamknutá, je možné ju zamknúť zámkom typu WRITE pre výhradný prístup bez zaradenia do frontu
  • V opačnom prípade je požiadavka zaradená do frontu WRITE
  • Pre pokus o uzamknutie tabuľky pre čítanie READ platí nasledujúci postup:
  • Ak s tabuľkou nepracujú iné vlákna v režime výhradného prístupu WRITE, môžeme tabuľku zamknúť zámkom READ bez zaradenia do frontu
  • v opačnom prípade je požiadavka zaradená do frontu READ

Pri každom uvoľnení tabuľky dôjde k rozdeľovaniu prístupu. Pozor! Požiadavky vo fronte WRITE majú prednosť pred požiadavkami vo fronte READ! V prípade požiadavky uzamknutia tabuľky s výhradným prístupom sú teda omeškania minimálne.
MySQL umožní zamknúť tabuľku pre čítanie (READ) iba vtedy, ak už nečaká žiadna požiadavka vo fronte WRITE.
Je to z toho dôvodu, že pre väčšinu aplikácií je najdôležitejšia aktualizácia dát! Keby sme z určitých dôvodov chceli uprednostniť čítanie pred zápisom, môžeme toto zmeniť pomocou predvoľby LOW_PRIORITY WRITE.
Ak vytvárame aplikáciu s veľkým vyťažením prúdov požiadaviek READ, nesmieme zabudnúť zaistiť dostatočný priestor na vykonanie požiadaviek WRITE. Inak by sa mohlo stať, že pre samotné čítanie nebude času na aktualizáciu databáz a používatelia budú dostávať neaktuálne informácie.
Niekedy bude veľmi potrebné čítať z tabuľky, ktorú si iné vlákno výhradne zamklo pre svoj zápis zámkom WRITE. Na to slúži parameter SELECT HIGH_PRIORITY. Tento príkaz však používajme iba vo veľmi vážnych prípadoch.

ODOMKNUTIE TABULIEK. Príkazom UNLOCK TABLES odomkneme všetky tabuľky, ktoré sme príslušným vláknom zamkli. Tabuľky sa automaticky odomknú aj v týchto prípadoch:

  • ak dané vlákno pošle ďalšiu požiadavku LOCK TABLES
  • ak sa spojenie s databázovým serverom preruší

Uzamknutie tabuliek nie je obmedzené žiadnym časovým limitom!

AKO POUŽÍVAŤ PRÍKAZ LOCK TABLES. V MySQL existuje niekoľko dobrých dôvodov, prečo by sme mali tabuľky pred použitím zamykať:

  • Viacnásobný prístup k tabuľke

Vlákno môže tabuľky zamknúť a tým zaistiť, že bude mať k vybraným tabuľkám výhradný prístup. Práca vo výhradnom režime mu zaručuje, že môže začaté operácie dokončiť bez rizika, že sa upravované záznamy pokúsi medzitým pozmeniť niektoré iné vlákno. Čím viac príkazov SQL príslušný kód obsahuje, tým je zamykanie tabuliek dôležitejšie. Možnosť vzniku konfliktov je najzávažnejší dôvod zamykania.

  • Výkon

Aplikácia môže v určitých prípadoch vyžadovať vykonanie niekoľkoriadkových operácií vo viacerých tabuľkách. Aplikácia bude rýchlejšia, ak sa pred spustením procedúry vybrané tabuľky uzamknú. Zaistí sa tak úspešné dokončenie začatej operácie.
Najväčší dosah na výkon majú viacnásobné výskyty príkazu INSERT. Obyčajne sa vyrovnávacia pamäť indexu vyprázdni po vykonaní každého pridávacieho príkazu. Ak vybrané tabuľky zamkneme, vyrovnávacia pamäť sa vyprázdni až po vykonaní všetkých príkazov INSERT a následnom odomknutí tabuliek.

  • Chýba transakčné spracovanie

MySQL neobsahovala do verzie 4 transakčné spracovanie. Toto je možné vďaka zámkom čiastočne nahradiť nasledujúcim postupom: Po zamknutí vybraných tabuliek overíme všetky podmienky, ktoré by mohli ovplyvniť úspech operácie. Ak je všetko v poriadku, uložíme zmeny. Na záver odomkneme tabuľky.

  • Zálohovanie databázy

Pri práci s databázami by sme mali svoje dáta zálohovať a mať tak v dosahu konzistentnú a ucelenú kópiu aktuálneho stavu. Konzistencia je veľmi dôležitá. To znamená, že pri vytváraní záloh by nemalo byť žiadne vlákno aktívne. Pred vytváraním zálohy, ak už nie je vôbec možné „odpojiť“ databázu, by sme mali aspoň zamknúť zálohované tabuľky pre čítanie.
O zálohovaní sa dá toho povedať viac, a preto sa budeme touto témou zaoberať nabudúce.

 

Zobrazit Galériu

Nechajte si posielať prehľad najdôležitejších správ emailom

Mohlo by Vás zaujímať

Ako na to

Tipy a triky: Ako na snímku obrazovky na akomkoľvek počítači s Windows?

02.12.2016 00:13

Ak snímky obrazovky robíte často apotrebujete napríklad funkcie na posun stránok alebo snímanie zobrazenia pri vyššom rozlíšení displeja, zrejme používate nejakú špecializovanú aplikáciu. Väčšina použ ...

Ako na to 1

Tipy a triky: Ako aplikácii prednastaviť spúšťanie s administrátorskými právami?

30.11.2016 00:10

Väčšina aspoň trochu skúsenejších používateľov vie, že aj keď máte na operačnom systéme Windows vytvorený administrátorský účet, aplikácie pre bezpečnosť nefungujú vždy splnými administrátorskými práv ...

Ako na to 2

Tipy a triky: Ako vypnúť uzamykaciu obrazovku vo Windows 10?

29.11.2016 00:10

Rozčuľuje vás, že pred každým prihlásením doúčtu vášho počítača musíte prejsť uzamykacou obrazovkou? Windows 10 na tejto obrazovke ukazuje čas,dátum anejakú zaujímavú fotografiu zrôznych kútov sveta. ...

Žiadne komentáre

Vyhľadávanie

Kyocera - prve-zariadenia-formatu-a4-s-vykonom-a3

Najnovšie videá