# Transakce

## Transakční zpracování

**Ochrana integrity dat**
- stav databáze
	- dán hodnotami objektů databáze v daném okamžiku
	- objektem se rozumí
		- tabulka, záznam, položka a spojení

**Konzistentní stav databáze**
- podmínky definující konzistentní stav databáze jsou definovány integritními omezeními
	- jednoduché deklarativní formou
	- složitější procedurální formou

**Porušení integrity DB**
- porušení integrity DB = porušení konzistentního stavu DB
- příčiny
	- selhání technického/hw zařízení (disk)
	- selhání operačního systému
	- chyba v aplikačním programu
	- chybný zásah obsluhy
	- chybná data
- příčin porušení integrity je tolik, že je těžké jim zabránit
	- SŘBD je tedy vybaven
		- mechanismem detekce nekonzistentního stavu
		- mechanismem zotavení

**Ochrana integrity**
- prostředky poskytnuté OS
	- kopie souboru (zpravidla na různá média)
	- kontrolní bod (záloha všech souborů a případné obnovení)
- speciálně vyvinuté pro databázové systémy
	- transakce

**Transakce**
- posloupnost operací nad objekty databáze
	- z pohledu uživatele ucelená operace
- je
	- logickou jednotkou práce
	- jednotkou zotavení z chyb
- po dobu provádění transakce není databáze v konzistentním stavu
- příklad: změna čísla čtenáře
	- u čtenáře ale i všech jeho výpůjček

**Stavy transakce**
- aktivní (A)
	- stav od začátku provádění
- chybný (F)
	- v pokračování transakce nelze pokračovat
- částečně potvrzený (PC)
	- po provedení poslední elementární operace
	- transakce nebyla dosud potvrzena
- zrušený (AB)
	- po příkazu `ROLLBACK`, vrácení do původního stavu
- potvrzený (C)
	- úspěšné doknčení a potvrzení příkazem `COMMIT`

**Vlastnosti transakce**
- atomicita (**A**tomicity)
	- buď všechny operace nebo žádná
- konzistence (**C**onsistency)
	- DB v konzistentním stavu před i po transakci
- izolovanost (**I**solation)
	- paralelně probíhající transakce se nijak neovlivňují
- trvalost (**D**urability)
	- po úspěšném potvrzení transakce všechny změny v databázi nemohou být ztraceny (ani při havárii systému)

**Žurnál**
- základní nástroj pro ochranu transakcí
- znám též jako logovací protokol
- zapisují se do něj změny od posledního kontrolního bodu
- obecně obsahuje
	- identifikaci transakce
	- kdo změnu provádí
	- kdy je změna provedena
	- kterého objektu se týká
	- novou hodnotu objektu
	- někdy i starou/původní hodnotu objektu

**Metody používání žurnálu**
- dopředné procházení (`REDO`)
	- po chybě se vychází z kopie uložené v kontrolním bodě
	- postupně se provádí změny uložené v žurnálu až do chybového okamžiku
	- potřebujeme jen nové hodnoty objektů
- zpětné použití žurnálu (`UNDO`, `ROLLBACK`)
	- po chybě se vychází ze současného (nekonzistentního) stavu DB
	- využíváme starých hodnot v žurnálu do začátku chybné transakce
	- postupným zpětným odvíjením žurnálu se dojde k začátku chybné transakce

**Metody ochrany transakce**
- odložený zápis do databáze
	- tvorba žurnálu s odloženými realizacemi změn
	- v minulosti též **dvoufázové potvrzování**
- přímý zápis do databáze
	- tvorba žurnálu s bezprostřední realizací změn

**Odložený zápis do databáze**
- transakce nesmí měnit hodnoty objektů v databázi dříve, než je částečně potvrzena
- transakce nemůže být částečně potvrzená, pokud není vytvořen příslušný záznam o změnách v žurnálu
- žurnál nevyžaduje staré hodnoty objektů
- nevýhoda: pomalé
+ pokud došlo k chybě
	+ před uložením do žurnálu
		+ přechod ze stavu _aktivní_ do _částečně porvrzený_
		+ nedošlo ke změnám v databázi (zůstala v konzistentním stavu)
	+ po částečném potvrzení transakce
		+ přechod ze stavu _částečně potvrzený_ do stavu _potvrzený_
		+ všechny změny jsou uloženy v žurnálu
		+ na žurnál bude použita metoda `REDO`

**Strategie obnovení odloženého zápisu do DB**

- vytvoříme seznam `REDO`
- žurnál procházíme od kontrolního bodu do předu
	- narazíme-li na konec transakce, vložíme ji do `REDO`
- transakce v seznamu `REDO` zpracujeme metodou `REDO`
- nedokončené transakce řešit nemusíme, protože neprovedly žádný zápis do databáze

**Přímý zápis do databáze**
- změny v DB se provedou okamžitě v době potřeby před potvrzením transakce
- v žurnálu je nutné uchovat staré hodnoty objektů
	- v případě chyby se provádí _vycouvání_ transakce použitím metody `UNDO`
- výhoda: rychlé
+ při chybě disku, kde mám uložený žurnál, nelze použít
+ do žurnálu nutno zapisovat i operace čtení
	+ novou hodnotu objektu zapsanou dosud nepotvrzenou transakcí může číst jiná transakce
	+ při vycouvání aktuální transakce proto musí vycouvat i ty transakce, které četly nové hodnoty objektů
	+ to může vést až k tzv. kaskádovému rušení transakcí
		- známý také jako dominový efekt
		- nejedná se o nějak častý jev

**Strategie obnovení přímého zápisu do DB**
- vytvoříme dva seznamy: `REDO` a `UNDO`
	- do seznamu `UNDO` vložíme všechny transakce z kontrolního bodu
	- seznam `REDO` zůstane prázdný
- žurnál procházíme od kontrolního bodu do předu
	- narazíme-li na začátek transakce, vložíme ji do `UNDO`
	- narazíme-li na konec transakce, převedeme ji z `UNDO` do `REDO`
- transakce zpracujeme metodou odpovídající názvu seznamu, ve kterém se nachází

## Paralelní zpracování transakcí

**Moduly SŘBD pro zpracování transakcí**
- z pohledu synchronizace nás ze SŘBD zajímají tyto moduly
- modul řízení transakcí (RT)
	- transakce se na něj obracejí se žádostí o vykonání operace
- modul řízení obnovy databáze (RD)
	-  v databázi vykoná (realizuje) čtení/zápis podle požadavků plánovače
- plánovač
	- zabezpečuje synchronizaci požadavků více transakcí v RT
	- požadavky zařazuje do tzv. plánů (rozvrhů)

**Zadání příkladu**
- ve všech příkladech budeme označovat
	- objekty (na disku) písmeny z konce abecedy
	- interní proměnné ze začátku abecedy
- mějme
	- počáteční hodnotu `X := 10`
	- dvě transakce `T1` a `T2` mající shodnou činnost

| T1 | T2 |
| ---- | ---- |
| `A := read(X)` |  |
| `A := A + 5` |  |
| `write(X, A)` |  |
|  | `B := read(X)` |
|  | `B := A + 5` |
|  | `write(X, B)` |
- je několik možných plánů, kdy někde se může čtení z disku spustit dříve, než se zapíše předchozí výsledek

**Uspořádatelnost**
- serializovatelnost
- efekt paralelního zpracování transakcí musí být stejný, jako by byly transakce uspořádány v nějakém sériovém plánu

Příklad: původní hodnota `X := 10`

```python
# T1
A := read(X)
A := A + 5
write(X, A)
# T2
B := read(X)
B := B * 3
write(X, B)
```

- plán 1 (T1, T2), X = 45
- plán 2 (T2, T1), X = 35
+ paralelní běh transakcí může skončit jedním nebo druhým výsledkem

### Uzamykací protokoly

**Operace `LOCK(X)`**
- uzamknutí objektu X vyvolá transakce, aby ho ochránila před přístupem jiných transakcí
- objekt smí být uzamčen pouze jednou transakcí
- pokud chce objekt uzamknout ještě jiná transakce, je systémem pozastavena

**Operace `UNLOCK(X)`**
- odemknutí objektu X
- objekt může odemknout jen ta transakce, která jej uzamkla
- pokud na odemčení tohoto objetu čeká jiná transakce, může být opětovně spuštěna

**Dvoufázový (zamykací) protokol**
- lze dokázat, že uspořádatelnost plánu je zaručena, jestliže
	- objekt může být v každém okamžiku uzamčen pouze pro jednu transakci
	- jakmile transakce odemkne alespoň jeden objekt, nesmí už žádný objekt uzamknout
- poznámka
	- nesmí uzamknout ani ten samý objekt, co odemkla (prostě nesmí žádný objekt uzamknout)
	- porušení dvoufázového protokolu hrozí v případě, že si sami definujete a řídíte transakce
+ Two-phase locking (2PL)
+ fáze 1: růst (expanding)
	+ transakce smějí pouze zamykat objekty, nesmí je odemykat
+ fáze 2: smršťování (shrinking)
	+ transakce smějí pouze objekty odemykat, nesmí žádný objekt uzamknout

**Zamykání objektů**
- databázové objekty lze zamykat ve dvou režimech
	-  výlučný, též exklusivní režim
	-  sdílený režim
- výlučný režim
	- objekt je uzamčen pro operace čtení i zápis
		- nemůže být současně uzamčen jinou transakcí
- sdílený režim
	- objekt je uzamčen jen pro operaci čtení
		- daný objekt může být současně uzamčen v režimu čtení další transakcí

**Uváznutí** (deadlock)
- dodržení dvoufázového protokolu ještě neznamená odstranění všech potíží
- zásadní výhoda DB: transakce umí `ROLLBACK`

| T1        | T2        |
| --------- | --------- |
| `LOCK(X)` |           |
|           | `LOCK(Y)` |
| `LOCK(Y)` |           |
|           | `LOCK(X)`          |
## Další protokoly

Musíme znát nějakou další informaci o objektech databáze, např. způsob jejich uložení

**Stromový uzamykací protokol** (Tree protocol)
- používá zámky
- pro hierarchické databáze
- reprezentace tzv. grafového protokolu
1. první výlučný zámek transakce T lze použít na jakýkoliv objekt X
2. další objekt může být uzamčen transakcí T pouze když byl v T uzamčen jeho předchůdce
3. objekty mohou být odemknuty kdykoliv
4. objekt, který byl v transakci T uzamknut a odemknut nesmí být už v T následovně znovu uzamknut
- všechny plány jsou uspořádatelné a nemůže dojít k uváznutí

**Protokol časových značek**
-  místo zámků používá tzv. časové značky ČZ
	- předává ji systém, tvoří rostoucí posloupnost
- plánovač transakcí vykonává konfliktní transakce (pracují nad stejným objektem X) v pořadí jejich ČZ
	- takto navržené plány jsou uspořádatelné
- u každého objektu X jsou zaznamenávány největší značky
	- pro čtení R_ČZ(X)
	- pro zápis W_ČZ(X)

## Transakce v praxi

**Zajištění konzistence v SŘBD Oracle**
- konzistentní čtení (Multiversion Read consistency)
	- na úrovni příkazu (Statement-Level Read Consistency)
		- data získaná dotazem jsou vždy potvrzená
	- na úrovni transakce (Transaction-Level Read Consistency)
		- každý příkaz transakce vidí data nová, která byla transakcí zapsána
		- ostatní transakce vidí data, která byla dostupná před začátkem transakce
- ke správě konzistentního čtení se využívají tzv. návratové segmenty
	- uloženy v tzv. návratovém tabulkovém prostoru

**Nežádoucí jevy při čtení dat v transakcích**

- **Špinavé čtení** (Dirty read)
	- též nepotvrzené čtení (Uncommited read)
	- příklad
		- transakce T1 aktualizuje vybraný záznam
		- transakce T2 následně přečte tuto aktualizovanou hodnotu dříve, než proběhne její potvrzení
	- problém nastane, když transakce T1 bude zrušena, tj. zavolá `ROLLBACK`
		- potom transakce T2 přečetla hodnotu, která neměla existovat

+ **Neopakovatelné čtení** (Non-repeatable read)
	- nastane, pokud čteme opakovaně stejnou hodnotu, ale v různých časech obdržíme různé hodnoty
	- příklad
		- transakce T1 přečte vybraný záznam
		- transakce T2 následně daný záznam aktualizuje a bude potvrzena
		- opětovné čtení stejného záznamu transakce T1 poskytne jiná data nebo také žádná

- **Fantomové čtení** (Phantom read)
	- nastane, pokud při opakovaném čtení získáme nová data, která nebyla dostupná při prvním čtení
	- příklad
		- transakce T1 přečte záznamy, které vyhovují dotazu
		- transakce T2 generuje nový záznam, která změní výsledek dotazu transakce T1
		- opětovné vyhodnocení stejného dotazu transakce T1 poskytuje data navíc

**Úrovně izolace transakce dle normy**

- **Serializovatelná** (serializable)
	- nejvyšší stupeň izolace
	- dovoleno vykonávat pouze uspořádatelné plány
	- transakce čeká, dokud záznamy uzamčené pro zápis ostatními transakcemi budou odemčeny
	- transakce drží zámky buď pro čtení anebo pro zápis pro rozsah záznamů, které chce buď číst anebo ovlivnit zápisem
		- rozsahem záznamů se rozumí výsledek selekce transakce
		- pokud jiná transakce vloží/aktualizuje/smaže záznam, který do daného rozsahu zapadá, nebude jí zápis povolen
	- transakce uvolňuje všechny zámky po jejím potvrzení nebo odvolání

+ **Opakovatelné čtení** (repeatable read)
	- transakce čeká, dokud záznamy uzamčené pro zápis ostatními transakcemi budou odemčeny
	- transakce drží
		- zámky pro čtení pro záznamy, které chce číst
		- zámky pro zápis pro záznamy, které chce vkládat/aktualizovat/mazat
	- jiná transakce může vložit/aktualizovat ty záznamy, které spadají do rozsahu záznamů uzamčených pro čtení
	- transakce uvolňuje všechny zámky po jejím potvrzení nebo odvolání

- **Potvrzené čtení** (read commited)
	- transakce čeká, dokud záznamy uzamčené pro zápis ostatními transakcemi budou odemčeny
	- transakce drží
		- zámek pro čtení pro aktuálně čtený záznam
		- zámek pro zápis záznamu, který chce aktualizovat/mazat
	- transakce uvolňuje
		- zámek pro čtení po přečtení aktuálního záznamu
		- zámky pro zápis po potvrzení nebo odvolání transakce

+ **Nepotvrzené čtení** (read uncommited)
	- nejnižší stupeň izolace
	- transakce není izolovaná od ostatních transakcí
	- obvykle je tato úroveň použita u transakcí v režimu čtení
		-  čte i nepotvrzené záznamy jiných transakcí

|                         | špinavé čtení  | neopakovatelné čtení | fantomové čtení |
| ----------------------- | -------------- | -------------------- | --------------- |
| serializovatelná úroúveň | nevyskytuje se | nevyskytuje se       | nevyskytuje se  |
| opakované čtení         | nevyskytuje se | nevyskytuje se       | VYSKYTUJE SE    |
| potvrzené čtení         | nevyskytuje se | VYSKYTUJE SE         | VYSKYTUJE SE    |
| nepotvrzené čtení       | VYSKYTUJE SE               | VYSKYTUJE SE                     | VYSKYTUJE SE                |
**Izolace transakce v SŘBD Oracle**
- Read Commited Isolation Level
	-  výchozí úroveň
	- transakce vidí data dostupná před začátkem transakce
	- dovoluje jevy neopakovatelné a fantomové čtení
	- trpí efektem ztráty aktualizace dat (lost update)
- Serializable Isolation Level
	- transakce vidí potvrzené změny v čase začátku transakce a změny provedené v transakci
	- brání všem nežádoucím jevům čtení, netrpí efektem ztráty aktualizace dat
- Read-Only Isolation Level
	- podobné jako předchozí úroveň
	- transakce nemají přístup k datům, které modifikují jiné transakce

**Ztráta aktualizace**
- nastane, pokud souběžné transakce modifikují hodnotu stejného záznamu
	- zůstane zapsaná až poslední změna, předchozí se ztratí
- příklad
	- transakce T1 modifikuje hodnotu konkrétního záznamu
	- transakce T2 také později chce modifikovat hodnotu stejného záznamu, ale je pozastavena
	- transakce T1 bude poté potvrzena, následně dojde k modifikaci dat transakce T2
	- transakce T2 bude potvrzena

**Nastavení úrovně izolace**
- na úrovni transakce
	```sql
	SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
	SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
	SET TRANSACTION ISOLATION LEVEL READONLY;
	```

- na úrovni aktuálního spojení
	```sql
	ALTER SESSION SET ISOLATION_LEVEL READ COMMITTED;
	ALTER SESSION SET ISOLATION_LEVEL SERIALIZABLE;
	ALTER SESSION SET ISOLATION_LEVEL READONLY;
	```