Transakce
Transakce
Transakce, zjednodušeně řečeno, je povel nebo sada povelů, které se buď provedou všechny a celé, nebo se neprovede vůbec nic. Transakce je nedělitelná (atomická) jednotka.
Každý jednotlivý SQL příkaz je transakcí. Tj. buď se provede celý, nebo nic. Toto platí, pokud jsou transakce podporovány. MyISAM tabulka z MySQL transakce nepodporuje, takže vám toto nezajišťuje.
Když spustíte nějaký příkaz, napříkad UPDATE
, musí se provést celý
nebo nic. To znamená, že když například updatujete 10 řádek, musí se změnit všech
10, nebo žádná. A to, i kdyby průběh příkazu přerušil výpadek proudu. DBMS si
při provádění loguje změny (transakční protokol) a v případě, že dojde k nějaké havárii,
je schopný z logu vrátit nedokončený příkaz (zruší všechny nepotvrzené operace).
Pokud jde o transakce s více SQL příkazy, představte si následující problém: Do banky příjdou dva požadavky na stržení peněz z účtu jednoho klienta. První požadavek si načte zůstatek klienta (a zjistí, že peněz má dost). O milisekundu později si načte zůstatek druhý požadavek. První požadavek odečte částku z načteného zůstatku a uloží ji. Totéž udělá o milisekundu i druhý požadavek. Výsledkem je, že je uložený výsledek druhého požadavku a stržení částky prvního požadavku zmizí. Tak takhle ne!
Nebo jiný příklad: chcete převést peníze z jednoho účtu na druhý. Nejdříve peníze z prvního účtu smažete. Pak se chystáte je připsat na druhý účet, ale v tom dojde k nějaké chybě a už se vám to nepodaří. Peníze nenávrantě zmizely. Tak tak by to taky nešlo. Buď se musí provést obojí (smazání z prvního účtu a připsání na druhý), nebo nic!
Začátek a konec transakce
Každá transakce má svůj začátek a konec. Transakce končí svým potvrzením (COMMITem).
Pokud pracuje databáze v tzv. auto commit režimu, pak je každý jednotlivý příkaz transakcí. Jeho spuštěním transakce začne a jeho dokončením se transakce automaticky ukončí (UPDATE začne updatováním prvního řádku a skončí updatem posledního řádku.)
Zajímavější je možnost vytvořit transakci z více příkazů. Prakticky se to dělá zahájením
transakce příkazem BEGIN
, pak spuštěním všech SQL příkazů, které mají být
v rámci transakce (které se mají buď všechny provést, nebo žádný) a nakonec potvrzením
transakce příkazem COMMIT
.
Když nejste v auto commit režimu, pak (dle ANSI/ISO normy)
transakce začne prvním SQL příkazem (po přihlášení k databázi) a automaticky se potvrdí (COMMITne)
při bezchybném ukončení programu (když ho ukončíte sami, neukončí se nějakou chybou nebo výpadkem proudu atd.).
Když databáze selže, provede se automaticky zrušení celé transakce (ROLLBACK).
Mimo to můžete samozřejmě potvrdit transakci kdykoliv příkazem COMMIT
.
(Následujícím SQL příkazem po COMMITu začíná nová transakce …).
ANSI/ISO norma po DBMS vyžaduje, aby byli součástí transakce všechny SQL příkazy. Realita je obvykle taková, že se v DBMS potvrzuje transakce jakýmkoliv SQL příkazem, který mění strukturu databáze (CREATE TABLE, ALTER TABLE …). (Takže nejen COMMITem). Důvodem je zřejmě velká náročnost, kterou by splnění požadavku ANSI/ISO normy vyžadovalo.
Souběh transakcí
Problémy, které mohou nastat při práci více uživatelů se stejnými daty lze rozdělit do následujících kategorií:
1. Problém ztracené aktualizace
Aktualizace jsou provedené bez vzájemného ohledu. V takovém případě platí poslední provedený UPDATE (DELETE …). Jde třeba o problém, který jsem popisoval v příkaldu se dvěma požadavky na stržení peněz z účtu jednoho klienta.
2. Problém nepotvrzených dat
Jde o tzv. nečisté čtení (dirty reading). Jde o problém, kdy si někdo přečte změny v datech, které jste ještě nepotvrdili (necommitli). Vy potom změny nepotvrdíte, ale ten kdo si je přečet je má za platné a pracuje s nimi.
3. Problém nekonzistentních dat
Jde o tzv. neopakovatelné čtení (nonrepeatable read). Během transakce provedete nějaký
SELECT
. Pak dál něco provádíte a mezi tím někdo provede
(a commitne) změnu v databázi. Vy pak provedete (během jedné transakce) stejný
SELECT
a on vám vrátí jiný výsledek.
4. Problém přízračných dat
Aneb problém vložení přeludu (phantom read). Jde o podobný problém, jako u neopakovatelného čtení. Týká se však SELECTů s agregačními funkcemi.
Problém nekonzistentních datj lze technicky ošetřit například tak, že si DBMS pamatuje obsah řádků, na které jste se (v transakci) už ptali (které četl). Dotaz na stejný řádek vrátí stejné data.
Pokud se zeptáte na SUMu řádků z nějaké tabulky, DBMS si zapamatuje hodnoty v řádcích, ze kterých
dělal SUMu. Když ale někdo mimo transakci vloží nový řádek a vy znovu provedete SUMu, bude tento nový
řádek součástí výsledku – stejný SELECT
vrátí jinou hodnotu.
Řešení souběhu transakcí
Izolace transakcí
- v průběhu transakce je pohled na DB zcela konzistentní
- v transakci nejsou vidět nepotvrzené změny jiných uživatelů
- v transakci nejsou vidět potvrzené změny jiných uživatelů (potvrzené v průběhu izolované transakce)
Serializace transakcí
- transakce A, B jsou spuštěny souběžně
- DBMS zajistí, že výsledky budou stejné, jako by transakce A proběhla první a za ní transakce B (NEBO nejdřív B a pak A). To znamená, že druhá transakce v pořadí musí ovykle čekat na dokončení té první
Odstínění transakcí
- skrytí nepotvrzených transakcí
- skrytí transakcí potvrzených po zahájení aktuální transakce
Úrovně izolací definovaných standardem
Následující tabulka ukazuje úrovně izolací definovaných standardem a jaké konflikty daná úroveň povoluje:
1. | 2. | 3. | 4. | |
READ_UNCOMITED | NE | ANO | ANO | ANO |
---|---|---|---|---|
READ_COMITED | NE | NE | ANO | ANO |
REPEATABLE_READ | NE | NE | NE | ANO |
SERIALIZABLE | NE | NE | NE | NE |
Všiměte si, že žádná z úrovní izolací nedovoluje přepsat data dvěma souběžnými transakcemi. Důsledkem je, že pokud se jakýkoliv program v jakékoliv úrovni izolace pokusí přepsat/smazat data již přepsaná/smazaná, dojde k odvolání transakce (celá se zruší).
Vždycky musíte počítat s tím, že každá transakce může být odvolána! Když budete psát nějaký program, musíte takovou situaci ošetřit a pokusit se transakci provést znovu, nebo zobrazit uživeteli chybovou hlášku atd.
ACID
ACID je zkratka pro vlastnosti, které by měla databázová transakce splňovat:
- A – Atomicity – buď se provede celá transakce, nebo nic.
- C – Consistency – před a po dokončení transakce není porušeno žádné integritní omezení a data jsou konzistentní.
- I – Isolation – výsledky příkazů uvnitř transakce jsou pro okolí neviditelné.
- D – Durability – trvalost – pokud byla transakce potvrzena, pak jsou změny dat skutečně uložené a už nemohou být ztracené.
To by bylo k teorii transakcí asi tak všechno. V kapitole Transakce prakticky se dozvíte, jak se to všechno dělá v Postgresu (a dalších DBMS) prakticky. Tato kapitola ale není teoretická, takže je zařazena až v sekci pro pokročilé. Pokud jste ale nedočkaví, můžete na tu kapitolu přeskočit hned.