Egymásnak ellentmondó adatok
Az erőforrásainkat szeretjük megosztani több felhasználó között, a gazdaságosabb, hatékonyabb működés érdekében. Egy erőforrás használata konfliktushoz vezethet, ha egyszerre akarja használni két vagy több felhasználó. Az adatbázisainkat általában egyszerre többen használjuk, az egyes adatbázis műveletek szintén konfliktusba kerülhetnek egymással.
Egyben kezelt műveletsor, amelyre teljesülnek az ACID elvek.
Az adatok védelme érdekében a tranzakciókezelés során két feladat van:
A tranzakciók tárgyalásánál gyakran használnak egy banki átutalást példaként:
folyamat1 | |
---|---|
beolvas Béla fizetése | 5000 |
Béla fizetését megemeljük 2000-rel | 7000 |
Kiírjuk a fizetést | 7000 |
folyamat2 | |
---|---|
beolvas Béla fizetése | 7000 |
Béla fizetését megemeljük 2000-rel | 9000 |
Kiírjuk a fizetést | 9000 |
f1 | beolvas Béla fizetése | 5000 |
f1 | Béla fizetését megemeljük 2000-rel | 7000 |
f2 | beolvas Béla fizetése | 5000 |
f2 | Béla fizetését megemeljük 2000-rel | 7000 |
f1 | kiír Béla fizetése | 7000 |
f2 | kiír Béla fizetése | 7000 |
Az utasítások oszthatatlanul hajtódnak végre.
Jellemzők:
create database tran character set utf8 collate utf8_hungarian_ci; create table dolgozok ( az int not null primary key auto_increment, nev varchar(20), fizetes double ); insert into dolgozok (nev, fizetes) values ('Béla', 5000), ('Tibi', 5000);
start transaction; update dolgozok set fizetes = 16000; update dolgozok set fizetes = fizetes + 10000 where id = 2; select * from dolgozok; rollback select * from dolgozok;
Két terminálban:
terminál1 | terminál2 |
---|---|
select * from dolgozok | select * from dolgozok |
terminál1 | terminál2 |
---|---|
start transaction | start transaction |
terminál1 | terminál2 |
---|---|
update dolgozok set fizetes=11000 az = 2 | update dolgozok set fizetes=12000 az = 1 |
select * from dolgozok | select * from dolgozok |
Mivel a két tranakció el van szigetelve egymástól, a terminál1-ben nem látszik még a terminál2 módosítása, és a terminál2-ben nem látszik még termnál1 módoítása.
terminál1 | terminál2 |
---|---|
update dolgozok set nev='Emese' az=1 |
A terminál1 várakozik, mert az 1-s rekordot a terminál2 már módosította.
terminál1 | terminál2 |
---|---|
commit |
A terminál2 végzett. Nézzük meg baloldalon:
terminál1 | terminál2 |
---|---|
select * from dolgozok |
Nézzük meg a terminál2-ben:
terminál1 | terminál2 |
---|---|
select * from dolgozok |
A terminál2-ben még nem látható a terminál1 Emese névváltoztatása.
terminál1 | terminál2 |
---|---|
commit | |
select * from dolgozok | select * from dolgozok |
Most már az állapotok egyeznek.
terminál1 | terminál2 |
---|---|
start transaction | start transaction |
terminál1 | terminál2 |
---|---|
update dolgozok set nev=„Irén” where az=1 | update dolgozok set nev=„Kati” where az=2 |
terminál1 | terminál2 |
---|---|
update dolgozok set fizetes=20000 where az=2 |
A terminál1 várakozásra kényszerül.
Terminál2-ben:
terminál1 | terminál2 |
---|---|
update dolgozok set fizetes=40000 where az=1 |
Most keresztbe várakoznak.
Példa:
Legyen a következő két tábla:
CREATE TABLE IF NOT EXISTS `Dolgozok` ( `az` int(11) NOT NULL, `nev` varchar(28) DEFAULT NULL, `anyja_neve` varchar(23) DEFAULT NULL, `telepules` varchar(20) DEFAULT NULL, `lakcim` varchar(46) DEFAULT NULL, `fizetes` double DEFAULT NULL, `szuletes` date DEFAULT NULL, `jutalom` double DEFAULT NULL ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `Naplo` ( `az` int(11) NOT NULL, `telepules` varchar(100) COLLATE utf8_hungarian_ci NOT NULL, `osszeg` double NOT NULL, `megjegyzes` text COLLATE utf8_hungarian_ci NOT NULL, `datum` date NOT NULL ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_hungarian_ci;
Feltöltjük, a Dolgozok táblát, úgy hogy legyen miskolci dolgozó, majd a következő SQL utasítást adjuk ki:
start transaction; SELECT @A:=sum(fizetes) FROM `Dolgozok` where telepules="Miskolc"; insert into Naplo (osszeg, telepules, datum) values (@A, "Miskolc", "2014-09-23"); commit;
Olyan adatokat is olvashatunk, amelyek még nincsenek rögzítve. Piszkos olvasás. A tranzakció vége előtt már olvashatók a nem rögzített adatok.
Olvasáskor mindig a már rögzített (commitelt) eredményt kapjuk. Ha fut egy tranzakció ami már változtatott az általunk kívánatos sorokon, akkor mi a régi eredményeket kapjuk. Ha sokan írják az adatbázist, itt lassulás következhet be, más folyamatok arra várnak, hogy az egész tábla frissítése befejeződjön.
Előforulhat a Phantom read és Non-repeatable reads hiba.
A Phantom read jelentése a következő: Előfordulhat, hogy két lekérdezés lefut és más eredményt adnak.
A Non-repeatable reads azt jelenti, hogy egy tranzakció még nem fejeződött be, amikor egy másik már megváltoztatta az olvasott adatokat.
Alapértelmezett szint MS SQL Server esetén.
Alapértelmezett szintje az InnoDB tábláknak. Csak rögzített rekordokat olvasunk. Vagyis nem várunk a teljes tábla frissítésére, ami már rögzített rekord az olvasható is.
Előfordulhat a Phantom read hiba.
Más tranzakció nem frissíthet, nem nézhet a táblába, amíg véget nem ér egy tranzakció. Olyan mint a zárolt (LOCK) tábla.
Az InnoDB mind a négy szintet támogatja. De vigyázzunk konvertáláskor, mert más adatbázis-kezelők esetleg nem támogatják mind a négy szintet.
Pl.:
A my.cnf állományban a [mysqld] szakaszban állíthatjuk:
[mysqld] transaction-isolation = {READ-UNCOMMITTED | READ-COMMITTED | REPEATABLE-READ | SERIALIZABLE}
Vagy egy SQL paranccsal is megadhatjuk amit szeretnénk:
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
A tranzakciós szint nem állítható tranzakción belül. Ha nem használjuk a session vagy a global kulcsot, a beállítás egy select elvégzéséig él csak.
Lehetséges beállítások:
set transaction isolation level read uncommitted; set transaction isolation level read committed; set transaction isolation level repeatable read; set transaction isolation level serializable;
Ellenőrzés:
select @@global.tx_isolation, @@tx_isolation
Különbségek a session, global és mindkettő elhagyása között:
create database tran character set utf8 collate utf8_hungarian_ci; create table dolgozok ( az int not null primary key auto_increment, nev varchar(20), fizetes double ); insert into dolgozok (nev, fizetes) values ('Béla', 5000), ('Tibi', 5000);
Indítsunk egy terminálban egy kapcsolatot:
start transaction; update dolgozok set fizetes=2000;
Egy másik terminálban, vizsgáljuk meg milyen izolációs szint van beállítva a munkameneten:
select @@tx_isolation;
Kérdezzük le a tran adatbázis dolgozók táblájának tartalmát:
select * from dolgozok;
Állítsuk be a read uncommitted izolációs szintet a munkamenetre:
set session transaction isolation level read uncommitted;
Most kérdezzük le újból:
select * from dolgozok;
Az első lekérdezésnél, ha az izolációs szint repeatable read volt, az update eredménye még nem látszik. A második lekérdezésnél, azonban meg kell jelenjen az első terminálon beállított új érték. A read uncommitted izolációs szint működik.