Nahoru
 

Kolize dat při práci s databázemi

Značná část současných aplikací nějakým způsobem používá databáze jako prostor pro ukládání a práci s daty. Je však bezpečné nechat aplikaci ovládat více lidí z různých zařízení, když všechny obsluhuje pouze jedna databáze? Bohužel, bezpečné to není a musíme se vypořádat s problémy, které mohou vzniknout.

U aplikací ovládaných z mnoha různých míst naráz může vzniknout kolize dat. Kolize dat zjednodušeně znamená, že si uživatelé navzájem přepisují data pod rukama. Pro snazší pochopení kolize si ukažme následující příklad.

Představme si firemní aplikaci, která vede záznamy zaměstnanců. Program má mimo jiné za úkol počítat každému zaměstnanci výši jeho bonusu k výplatě. Bonus může zaměstnanci udělit kdokoliv z jeho nadřízených. Pokud se nadřízený rozhodne přidat někomu bonus, v aplikaci si vybere určitého zaměstnance a zadá výši odměny. Po potvrzení se k současnému bonusu přičte nová částka a aplikace jej uloží do databáze. Každý měsíc se bonusy vyplatí a částky v databázi vynulují.

Ukázka průběhu navýšení bonusu
Obrázek č. 1: Průběh navýšení bonusu

Aplikace pro aktualizaci bonusu potřebuje jeho současnou hodnotu (aby věděla, k čemu přičítat), na kterou se dotáže databáze. Poté k této hodnotě přičte zadané navýšení a nový bonus pošle databázi k uložení.

Představme si, že nadřízení Aleš Jirák a Emil Chmelný chtějí udělit Luďku Kopřivovi každý vlastní bonus. Nešťastnou náhodou se jim to povede udělat ve stejnou chvíli a přihodí se následující věc: Aleš Luďkovi přidává jako bonus 1200 Kč. Po odeslání požadavku aplikace v databázi zjistí současnou hodnotu bonusu 700 Kč, k čemuž přičte zadané navýšení 1200 Kč a výsledek 1900 Kč uloží do databáze. Ve stejné chvíli jako Aleš, Emil také přidává bonus Luďkovi, a to ve výši 600 Kč. Po potvrzení přidání se aplikace dotáže na současnou hodnotu bonusu Luďka, kde by už mělo být 1900 Kč, ale je tam 700 Kč, protože bonus se ještě nestihl aktualizovat. Aplikace tedy pokračuje tak, že k 700 Kč přičte 600 Kč a výsledných 1300 Kč uloží do databáze. Mezitím, co aplikace na straně Emila počítala novou hodnotu bonusu, se do databáze uložila hodnota 1900 Kč od Aleše. Ta ale byla přepsána hodnotou od Emila, takže po provedení obou těchto udělení bonusů máme v databázi hodnotu 1300 Kč. To je očividně špatně, protože jsme Luďka připravili o 1200 Kč. Těmto situacím říkáme kolize dat (data concurrency).

Průběh udělení bonusů s neošetřenou kolizí
Obrázek č. 2: Průběh udělení bonusů s neošetřenou kolizí

Na první pohled se může zdát, že tento problém je zcela ojedinělý a téměř není šance, aby nastávaly časté kolize. Přeci jen bychom museli mít velkou smůlu, abychom v rámci milisekund potvrdili aktualizaci dat. V příkladové aplikaci by vzhledem k jednoduchosti pravděpodobně k velkým problémům nedocházelo, avšak u velkých programů nebo webů, kde se v databázi aktualizují hodnoty velice často (např. velké eshopy), budeme mít na kolize daleko větší šance. Neošetřená kolize nás na nic neupozorní, prostě se zapíše špatná hodnota. Ba co víc, čím dál tím více aplikací nepracuje s databází takovým způsobem, jaký je popsán v příkladu. Naše příkladová aplikace se totiž na data ptá těsně před provedením operace, což pro všechny programy neplatí.

Stále častěji se setkáváme s případy programů, jež jsou „odpojené“ od databáze (většinou tak pracujeme při používání ORMObjektově relační mapování). Ty fungují tak, že si stáhnou data z databáze, uloží si je do operační paměti a od databáze se odpojí. Aplikace následně pracuje s daty, které si předtím uložila a s databází komunikuje pouze když chce data aktualizovat. Z toho důvodu ke kolizím dochází zcela běžně, protože mezi aktualizací dat při práci dvou různých uživatelů můžou uběhnout klidně hodiny.

Řešení kolizí dat

Kolize se dají řešit různými způsoby. U korektních aplikací vyžadující vždy správný výsledek (např. banky) bychom je určitě měli ošetřit. Obecně řešení závisí na použité technologii, avšak způsoby bývají podobné.

Jednoduché ošetření kolizí je zamčení databáze (Database Locking). Jakmile se někdo pomocí aplikace připojí k databázi, tak ji uzamkneme a ostatní uživatelé musí počkat, dokud změny nebudou hotové. Nejspíš není třeba vysvětlovat, že takové řešení není vhodné, ba naopak velice nepraktické. V podstatě omezíme použití aplikace s databází na jednoho uživatele naráz.

Dalším způsobem, jak se vypořádat s kolizemi, je kontrola aktuálnosti dat, jež jsme z databáze dostali. Jinými slovy zkontrolujeme, zda originální data, jež jsme získali z databáze, odpovídají těm, které jsou v databázi nyní. Pokud se shodují, tak víme, že nedošlo k žádné změně a můžeme v klidu pokračovat. Jestli však data nesouhlasí, muselo dojít k jejich aktualizaci. Poté program může uživatele upozornit, že nepracoval s aktuálními daty a jakýmkoliv vhodným způsobem pokračovat tak, aby se nic nerozbilo.

V našem příkladu s neošetřenou kolizí by pomocí kontroly aktuálnosti dat aplikace upozornila Emila Chmelného, že aplikace nepracovala s aktuálními daty a nenechala by ho jím vypočítanou hodnotu uložit do databáze. Konkrétně by aplikace mohla zjistit, že originální výše bonusu, jež si vzala z databáze (700 Kč) se nerovná aktuální výši bonusu v databázi (1900 Kč). Samotný způsob ošetření lze provést různými způsoby, důležité ale je, že nyní o kolizi víme.

Řešení kolizí není žádná novinka. Spousta databází s nimi počítá a dává nám nástroje, jak se s nimi vypořádat. Například databáze MSSQL má vlastní datový typ rowversion, který nám zajišťuje vygenerování unikátního čísla po každé aktualizaci řádku. Ten pak můžeme snadno použít pro kontrolu aktuálnosti dat. V jiných relačních databázích, kde není naimplementovaný žádný ekvivalent rowversion, si jej můžeme pomocí stored procedur nasimulovat.

Kolize dat mezi databázemi a aplikacemi, jež s nimi pracují se vyskytují zcela běžně a je třeba se s nimi vypořádat. Pokud tak neučiníme, může se stát, že v databázi budou špatná data a nikdo o nich nebude vědět. V některých aplikacích by neošetření takovýchto chyb mohlo vyjít velice draho, stačí si představit, co by se asi dělo, kdyby ke kolizím dat docházelo v bankovních systémech.