* Транзакции

Публикувано на 18 април 2009 от Филип Петров. Записано в DB.


Транзакция наричаме последователност от SQL заявки, която трябва да изпълняват условието или всичките да бъдат изпълнени или нито една от тях да не бъде изпълнена. Може да дадем класически пример с банковите транзакции. Например ако искаме да прехвърлим 50 лева от акаунт 1 в акаунт 2, то трябва да изпълним следните две заявки:

UPDATE accounts
SET amount = amount - 50
WHERE id = 1;

UPDATE accounts
SET amount = amount + 50
WHERE id = 2;

Какво обаче ще се случи ако например втората заявка не се изпълни (например възникне грешка)? Отговорът е, че парите ще бъдат изгубени. Тук се явява силата на транзакциите – те гарантират че ако някоя заявка не се изпълни, то данните ще бъдат възстановени в първоначален вид.

Групирането на заявки в транзакция се изпълнява изключително лесно. Единствено трябва да оградим данните с BEGIN (започване на транзакция) и COMMIT (край на транзакция):

BEGIN;

UPDATE accounts
SET amount = amount - 50
WHERE id = 1;

UPDATE accounts
SET amount = amount + 50
WHERE id = 2;

COMMIT;

Ако някоя от транзакциите пропадне, то се прави т.нар. ROLLBACK. За целта се използва innodb log файл, в който се записват старите данни преди изпълнението на всяка заявка. Естествено ние можем да правим ROLLBACK и сами. Например:

mysql> SELECT amount
FROM accounts
WHERE id = 1;
+--------+
| amount |
+--------+
| 106.38 |
+--------+
1 row in set (0.00 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE accounts
SET amount = amount - 50
WHERE id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> ROLLBACK;
Query OK, 0 rows affected (0.36 sec)

mysql> SELECT amount
FROM accounts
WHERE id = 1;
+--------+
| amount |
+--------+
| 106.38 |
+--------+
1 row in set (0.00 sec)

Виждате, че резултатите от заявката SELECT са едни и същи, тоест ROLLBACK е „върнал“ данните в първоначалния им вид преди заявката UPDATE.

Транзакциите трябва да отговарят на условие за консистентно четене. Това означава, че всеки SELECT чете данните записани точно след последния COMMIT. Ето един пример – отворили сме две връзки към базата данни. С едната започваме транзакция и правим UPDATE като даваме 200 лева на акаунт 1. След това във втората връзка искаме да проверим наличността на този акаунт:

Transactions Consitency

След това с първата връзка завършваме транзакцията и отново проверяваме каква е стойността във втората връзка:

Transactions Consitency

Важно е да споменем, че при InnoDB всяка заявка, която НЕ участва в блокове „BEGIN – COMMIT“ е автоматично записана, т.е. можем да приемем, че единичните заявки са завършени транзакции сами по себе си.

В следващата статия ще разгледаме „заключвания“ с цел синхронизация на транзакции.

Забележка: За стартиране на транзакция можете да използвате и по-популярната сред останалите бази данни „START TRANSACTION“ команда.



5 коментара за “Транзакции”

 
  1. cufi_pufi написа:

    Искам само да попитам първия и най-лесен пример за транзакции как ще стане на Access, прочетох, че се слага BEGIN TRANSACTION и COMMIT TRANSACTION, но пак не ми тръгва.

     
  2. Филип Петров написа:

    SQL стандартът попринцип казва „START TRANSACTION“ и „COMMIT“. Честно си признавам, че не знам как е в ACCESS.

     
  3. Филип Славов написа:

    Нещо не схващам обаче…
    Пиша това във MySQL конзолния прозорец:

    BEGIN;

    UPDATE Customer
    SET t_id = 4
    WHERE cus_id = 4;

    UPDATE Customer
    SET t_id = 5
    WHERE cus_id = 4213;

    COMMIT;

    Както се сещате на 2-рия UPDATE такова id няма и изплюва грешка там. Но в момента в който дам COMMIT промяната на първия UPDATE се изпълнява въпреки всичко. Нали идеята на транзакциите беше да се опаковат няколко заявки във едно (между Begin и Commit-а) за да се изпълнят всички заедно и така или се изпълняват всички или никоя не се изпълнява. Как така въпреки грешката се изпълнява първия UPDATE? Или ако не при какви условия би треело транзакцията да пропадне.

    ПП: Докато не дам commit, в базата ми няма промяна, проверих…
    ПП2: Ползвам ENGINE = InnoDB;
    ПП3: Нещо като напиша коментар не ми излизат след тва… Писах и за View преди неколко дена и все още не е излезъл..

     
  4. Филип Петров написа:

    Филип Славов – Здравей,

    Бъркаш цялата концепция. За MySQL това, че даден UPDATE връща „грешка“ (от твоя пример – няма такова id) не означава, че заявката не е изпълнена успешно. Не е сървъра за базата данни този, който взима решението кога транзакция е успешна и кога не – ние сме.

    В твоя пример си представи следното – тези заявки се подават от програма написана на С. След първата заявка резултатът се подава към програмата и той е „OK, 1 rows affected“, което за програмата означава „първата заявка е успешна“. След втората заявка обаче се връща грешка – тогава програмата вместо COMMIT ще подаде заявка ROLLBACK. Това е логиката. Надявам се, че се изразих ясно.

     
  5. Филип Славов написа:

    Аха… значи все пак ние ръчно решаваме дали да commit-нем, или Rollback-нем в зависимост от общия резултат на заявките (дали ни харесва или не :D )
    10x…

     

Trackback URI | RSS за коментарите

Пусни коментар