3.4. ТÑанзакÑии #
ТÑанзакÑии â ÑÑо ÑÑндаменÑалÑное понÑÑие во вÑÐµÑ Ð¡Ð£ÐÐ. СÑÑÑ ÑÑанзакÑии в Ñом, ÑÑо она обÑединÑÐµÑ Ð¿Ð¾ÑледоваÑелÑноÑÑÑ Ð´ÐµÐ¹ÑÑвий в Ð¾Ð´Ð½Ñ Ð¾Ð¿ÐµÑаÑÐ¸Ñ Â«Ð²ÑÑ Ð¸Ð»Ð¸ ниÑего». ÐÑомежÑÑоÑнÑе ÑоÑÑоÑÐ½Ð¸Ñ Ð²Ð½ÑÑÑи поÑледоваÑелÑноÑÑи не Ð²Ð¸Ð´Ð½Ñ Ð´ÑÑгим ÑÑанзакÑиÑм, и еÑли ÑÑо-Ñо помеÑÐ°ÐµÑ ÑÑпеÑно завеÑÑиÑÑ ÑÑанзакÑиÑ, ни один из ÑезÑлÑÑаÑов ÑÑÐ¸Ñ Ð´ÐµÐ¹ÑÑвий не ÑÐ¾Ñ ÑаниÑÑÑ Ð² базе даннÑÑ .
ÐапÑимеÑ, ÑаÑÑмоÑÑим Ð±Ð°Ð·Ñ Ð´Ð°Ð½Ð½ÑÑ Ð±Ð°Ð½ÐºÐ°, в коÑоÑой ÑодеÑжиÑÑÑ Ð¸Ð½ÑоÑмаÑÐ¸Ñ Ð¾ ÑÑеÑÐ°Ñ ÐºÐ»Ð¸ÐµÐ½Ñов, а Ñакже обÑие ÑÑÐ¼Ð¼Ñ Ð¿Ð¾ оÑделениÑм банка. ÐÑедположим, ÑÑо Ð¼Ñ Ñ Ð¾Ñим пеÑевеÑÑи 100 доллаÑов Ñо ÑÑÑÑа ÐлиÑÑ Ð½Ð° ÑÑÑÑ Ðоба. ÐÑоÑÑоÑÑ Ñади, ÑооÑвеÑÑÑвÑÑÑие SQL-ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ Ð¼Ð¾Ð¶Ð½Ð¾ запиÑаÑÑ Ñак:
UPDATE accounts SET balance = balance - 100.00
WHERE name = 'Alice';
UPDATE branches SET balance = balance - 100.00
WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Alice');
UPDATE accounts SET balance = balance + 100.00
WHERE name = 'Bob';
UPDATE branches SET balance = balance + 100.00
WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Bob');ТоÑное ÑодеÑжание команд здеÑÑ Ð½Ðµ важно, важно лиÑÑ Ñо, ÑÑо Ð´Ð»Ñ Ð²ÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÑÑой доволÑно пÑоÑÑой опеÑаÑии поÑÑебовалоÑÑ Ð½ÐµÑколÑко оÑделÑнÑÑ Ð´ÐµÐ¹ÑÑвий. ÐÑи ÑÑом Ñ ÑоÑки зÑÐµÐ½Ð¸Ñ Ð±Ð°Ð½ÐºÐ° Ð½ÐµÐ¾Ð±Ñ Ð¾Ð´Ð¸Ð¼Ð¾, ÑÑÐ¾Ð±Ñ Ð²Ñе ÑÑи дейÑÑÐ²Ð¸Ñ Ð²ÑполнилиÑÑ Ð²Ð¼ÐµÑÑе, либо не вÑполнилиÑÑ ÑовÑем. ÐÑли Ðоб полÑÑÐ¸Ñ 100 доллаÑов, но они не бÑдÑÑ ÑпиÑÐ°Ð½Ñ Ñо ÑÑÑÑа ÐлиÑÑ, обÑÑÑниÑÑ ÑÑо Ñбоем ÑиÑÑÐµÐ¼Ñ Ð¾Ð¿ÑеделÑнно не ÑдаÑÑÑÑ. РнаобоÑоÑ, ÐлиÑа вÑÑд ли бÑÐ´ÐµÑ Ð´Ð¾Ð²Ð¾Ð»Ñна, еÑли она пеÑеведÑÑ Ð´ÐµÐ½Ñги, а до Ðоба они не дойдÑÑ. Ðам нÑжна гаÑанÑиÑ, ÑÑо еÑли ÑÑо-Ñо помеÑÐ°ÐµÑ Ð²ÑполниÑÑ Ð¾Ð¿ÐµÑаÑÐ¸Ñ Ð´Ð¾ конÑа, ни одно из дейÑÑвий не оÑÑÐ°Ð²Ð¸Ñ Ñледа в базе даннÑÑ . Ð Ð¼Ñ Ð¿Ð¾Ð»ÑÑаем ÑÑÑ Ð³Ð°ÑанÑиÑ, обÑединÑÑ Ð´ÐµÐ¹ÑÑÐ²Ð¸Ñ Ð² Ð¾Ð´Ð½Ñ ÑÑанзакÑиÑ. ÐовоÑÑÑ, ÑÑо ÑÑанзакÑÐ¸Ñ Ð°ÑомаÑна: Ñ ÑоÑки зÑÐµÐ½Ð¸Ñ Ð´ÑÑÐ³Ð¸Ñ ÑÑанзакÑий она либо вÑполнÑеÑÑÑ Ð¸ ÑикÑиÑÑеÑÑÑ Ð¿Ð¾Ð»Ð½Ð¾ÑÑÑÑ, либо не ÑикÑиÑÑеÑÑÑ ÑовÑем.
Ðам Ñакже нÑжна гаÑанÑиÑ, ÑÑо поÑле завеÑÑÐµÐ½Ð¸Ñ Ð¸ подÑвеÑÐ¶Ð´ÐµÐ½Ð¸Ñ ÑÑанзакÑии ÑиÑÑемой баз даннÑÑ , ÐµÑ ÑезÑлÑÑаÑÑ Ð² Ñамом деле ÑÐ¾Ñ ÑанÑÑÑÑÑ Ð¸ не бÑдÑÑ Ð¿Ð¾ÑеÑÑнÑ, даже еÑли вÑкоÑе пÑоизойдÑÑ Ð°Ð²Ð°ÑиÑ. ÐапÑимеÑ, еÑли Ð¼Ñ ÑпиÑали ÑÑÐ¼Ð¼Ñ Ð¸ вÑдали ÐµÑ ÐобÑ, Ð¼Ñ Ð´Ð¾Ð»Ð¶Ð½Ñ Ð¸ÑклÑÑиÑÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑÑ Ñого, ÑÑо ÑÑмма на его ÑÑÑÑе воÑÑÑановиÑÑÑ, как ÑолÑко он вÑÐ¹Ð´ÐµÑ Ð·Ð° двеÑи банка. ТÑанзакÑÐ¸Ð¾Ð½Ð½Ð°Ñ Ð±Ð°Ð·Ð° даннÑÑ Ð³Ð°ÑанÑиÑÑеÑ, ÑÑо вÑе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ð¸ÑÑваÑÑÑÑ Ð² поÑÑоÑнное Ñ ÑанилиÑе (напÑимеÑ, на диÑк) до Ñого, как ÑÑанзакÑÐ¸Ñ Ð±ÑÐ´ÐµÑ ÑÑиÑаÑÑÑÑ Ð·Ð°Ð²ÐµÑÑÑнной.
ÐÑÑÐ³Ð°Ñ Ð²Ð°Ð¶Ð½Ð°Ñ Ñ Ð°ÑакÑеÑиÑÑика ÑÑанзакÑионнÑÑ Ð±Ð°Ð· даннÑÑ ÑеÑно ÑвÑзана Ñ Ð°ÑомаÑноÑÑÑÑ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹: когда одновÑеменно вÑполнÑеÑÑÑ Ð¼Ð½Ð¾Ð¶ÐµÑÑво ÑÑанзакÑий, ÐºÐ°Ð¶Ð´Ð°Ñ Ð¸Ð· Ð½Ð¸Ñ Ð½Ðµ Ð²Ð¸Ð´Ð¸Ñ Ð½ÐµÐ·Ð°Ð²ÐµÑÑÑннÑе изменениÑ, пÑоизведÑннÑе дÑÑгими. ÐапÑимеÑ, еÑли одна ÑÑанзакÑÐ¸Ñ Ð¿Ð¾Ð´ÑÑиÑÑÐ²Ð°ÐµÑ Ð±Ð°Ð»Ð°Ð½Ñ Ð¿Ð¾ оÑделениÑм, бÑÐ´ÐµÑ Ð½ÐµÐ¿ÑавилÑно, еÑли она поÑÑиÑÐ°ÐµÑ ÑаÑÑ Ð¾Ð´ в оÑделении ÐлиÑÑ, но не ÑÑÑÑÑ Ð¿ÑÐ¸Ñ Ð¾Ð´ в оÑделении Ðоба, или наобоÑоÑ. ÐоÑÑÐ¾Ð¼Ñ ÑвойÑÑво ÑÑанзакÑий «вÑÑ Ð¸Ð»Ð¸ ниÑего» должно опÑеделÑÑÑ Ð½Ðµ ÑолÑко, как Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÑÐ¾Ñ ÑанÑÑÑÑÑ Ð² базе даннÑÑ , но и как они Ð²Ð¸Ð´Ð½Ñ Ð² пÑоÑеÑÑе ÑабоÑÑ. ÐзменениÑ, пÑоизводимÑе оÑкÑÑÑой ÑÑанзакÑией, Ð½ÐµÐ²Ð¸Ð´Ð¸Ð¼Ñ Ð´Ð»Ñ Ð´ÑÑÐ³Ð¸Ñ ÑÑанзакÑий, пока она не бÑÐ´ÐµÑ Ð·Ð°Ð²ÐµÑÑена, а заÑем они ÑÑановÑÑÑÑ Ð²Ð¸Ð´Ð½Ñ Ð²Ñе ÑÑазÑ.
Ð Postgres Pro ÑÑанзакÑÐ¸Ñ Ð¾Ð¿ÑеделÑеÑÑÑ Ð½Ð°Ð±Ð¾Ñом SQL-команд, окÑÑжÑннÑм командами BEGIN и COMMIT. Таким обÑазом, наÑа банковÑÐºÐ°Ñ ÑÑанзакÑÐ¸Ñ Ð´Ð¾Ð»Ð¶Ð½Ð° бÑла Ð±Ñ Ð²ÑглÑдеÑÑ Ñак:
BEGIN;
UPDATE accounts SET balance = balance - 100.00
WHERE name = 'Alice';
-- ...
COMMIT;ÐÑли в пÑоÑеÑÑе вÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÑÑанзакÑии Ð¼Ñ ÑеÑим, ÑÑо не Ñ
оÑим ÑикÑиÑоваÑÑ ÐµÑ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ (напÑимеÑ, поÑÐ¾Ð¼Ñ ÑÑо оказалоÑÑ, ÑÑо Ð±Ð°Ð»Ð°Ð½Ñ ÐлиÑÑ ÑÑал оÑÑиÑаÑелÑнÑм), Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ вÑполниÑÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ ROLLBACK вмеÑÑо COMMIT, и вÑе наÑи Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð±ÑдÑÑ Ð¾ÑмененÑ.
Postgres Pro на Ñамом деле оÑÑабаÑÑÐ²Ð°ÐµÑ ÐºÐ°Ð¶Ð´Ñй SQL-опеÑаÑÐ¾Ñ ÐºÐ°Ðº ÑÑанзакÑиÑ. ÐÑли Ð²Ñ Ð½Ðµ вÑÑавиÑе ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ BEGIN, Ñо каждÑй оÑделÑнÑй опеÑаÑÐ¾Ñ Ð±ÑÐ´ÐµÑ Ð½ÐµÑвно окÑÑжÑн командами BEGIN и COMMIT (в ÑлÑÑае ÑÑпеÑного завеÑÑениÑ). ÐÑÑÐ¿Ð¿Ñ Ð¾Ð¿ÐµÑаÑоÑов, окÑÑжÑннÑÑ
командами BEGIN и COMMIT иногда назÑваÑÑ Ð±Ð»Ð¾ÐºÐ¾Ð¼ ÑÑанзакÑии.
ÐÑимеÑание
ÐекоÑоÑÑе клиенÑÑкие библиоÑеки добавлÑÑÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ BEGIN и COMMIT авÑомаÑиÑеÑки и неÑвно ÑоздаÑÑ Ð·Ð° Ð²Ð°Ñ Ð±Ð»Ð¾ÐºÐ¸ ÑÑанзакÑий. ÐодÑобнее об ÑÑом Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе ÑзнаÑÑ Ð² докÑменÑаÑии инÑеÑеÑÑÑÑего Ð²Ð°Ñ Ð¸Ð½ÑеÑÑейÑа.
ÐпеÑаÑоÑами в ÑÑанзакÑии можно Ñакже ÑпÑавлÑÑÑ Ð½Ð° более деÑалÑном ÑÑовне, иÑполÑзÑÑ ÑоÑки ÑоÑ
ÑанениÑ. ТоÑки ÑоÑ
ÑÐ°Ð½ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð·Ð²Ð¾Ð»ÑÑÑ Ð²ÑбоÑоÑно оÑменÑÑÑ Ð½ÐµÐºÐ¾ÑоÑÑе ÑаÑÑи ÑÑанзакÑии и ÑикÑиÑоваÑÑ Ð²Ñе оÑÑалÑнÑе. ÐпÑеделив ÑоÑÐºÑ ÑоÑ
ÑÐ°Ð½ÐµÐ½Ð¸Ñ Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ SAVEPOINT, пÑи необÑ
одимоÑÑи Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе веÑнÑÑÑÑÑ Ðº ней Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ ROLLBACK TO. ÐÑе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² базе даннÑÑ
, пÑоизоÑедÑие поÑле ÑоÑки ÑоÑ
ÑÐ°Ð½ÐµÐ½Ð¸Ñ Ð¸ до моменÑа оÑкаÑа, оÑменÑÑÑÑÑ, но изменениÑ, пÑоизведÑннÑе Ñанее, ÑоÑ
ÑанÑÑÑÑÑ.
Ðогда Ð²Ñ Ð²Ð¾Ð·Ð²ÑаÑаеÑеÑÑ Ðº ÑоÑке ÑÐ¾Ñ ÑанениÑ, она пÑÐ¾Ð´Ð¾Ð»Ð¶Ð°ÐµÑ ÑÑÑеÑÑвоваÑÑ, Ñак ÑÑо Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе оÑкаÑÑваÑÑÑÑ Ðº ней неÑколÑко Ñаз. С дÑÑгой ÑÑоÑонÑ, еÑли Ð²Ñ ÑвеÑенÑ, ÑÑо вам не пÑидÑÑÑÑ Ð¾ÑкаÑÑваÑÑÑÑ Ðº опÑеделÑнной ÑоÑке ÑÐ¾Ñ ÑанениÑ, ÐµÑ Ð¼Ð¾Ð¶Ð½Ð¾ ÑдалиÑÑ, ÑÑÐ¾Ð±Ñ ÑиÑÑема вÑÑвободила ÑеÑÑÑÑÑ. ÐомниÑе, ÑÑо пÑи Ñдалении или оÑкаÑе к ÑоÑке ÑÐ¾Ñ ÑÐ°Ð½ÐµÐ½Ð¸Ñ Ð²Ñе ÑоÑки ÑÐ¾Ñ ÑанениÑ, опÑеделÑннÑе поÑле неÑ, авÑомаÑиÑеÑки ÑниÑÑожаÑÑÑÑ.
ÐÑÑ ÑÑо пÑоиÑÑ Ð¾Ð´Ð¸Ñ Ð² блоке ÑÑанзакÑии, Ñак ÑÑо в дÑÑÐ³Ð¸Ñ ÑеанÑÐ°Ñ ÑабоÑÑ Ñ Ð±Ð°Ð·Ð¾Ð¹ даннÑÑ ÑÑого не видно. СовеÑÑÑннÑе дейÑÑÐ²Ð¸Ñ ÑÑановÑÑÑÑ Ð²Ð¸Ð´Ð½Ñ Ð´Ð»Ñ Ð´ÑÑÐ³Ð¸Ñ ÑеанÑов вÑе ÑÑазÑ, ÑолÑко когда Ð²Ñ ÑикÑиÑÑеÑе ÑÑанзакÑиÑ, а оÑменÑннÑе дейÑÑÐ²Ð¸Ñ Ð½Ðµ Ð²Ð¸Ð´Ð½Ñ Ð²Ð¾Ð¾Ð±Ñе никогда.
ÐеÑнÑвÑиÑÑ Ðº банковÑкой базе даннÑÑ , пÑедположим, ÑÑо Ð¼Ñ ÑпиÑÑваем 100 доллаÑов Ñо ÑÑÑÑа ÐлиÑÑ, добавлÑем Ð¸Ñ Ð½Ð° ÑÑÑÑ Ðоба, и вдÑÑг оказÑваеÑÑÑ, ÑÑо денÑги нÑжно бÑло пеÑевеÑÑи Уолли. Рданном ÑлÑÑае Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ пÑимениÑÑ ÑоÑки ÑÐ¾Ñ ÑанениÑ:
BEGIN;
UPDATE accounts SET balance = balance - 100.00
WHERE name = 'Alice';
SAVEPOINT my_savepoint;
UPDATE accounts SET balance = balance + 100.00
WHERE name = 'Bob';
-- оÑибоÑное дейÑÑвие... забÑÑÑ ÐµÐ³Ð¾ и иÑполÑзоваÑÑ ÑÑÑÑ Ð£Ð¾Ð»Ð»Ð¸
ROLLBACK TO my_savepoint;
UPDATE accounts SET balance = balance + 100.00
WHERE name = 'Wally';
COMMIT;ÐÑÐ¾Ñ Ð¿ÑимеÑ, конеÑно, неÑколÑко надÑман, но он показÑваеÑ, как можно ÑпÑавлÑÑÑ Ð²Ñполнением команд в блоке ÑÑанзакÑий, иÑполÑзÑÑ ÑоÑки ÑоÑ
ÑанениÑ. Ðолее Ñого, ROLLBACK TO â ÑÑо единÑÑвеннÑй ÑпоÑоб веÑнÑÑÑ ÐºÐ¾Ð½ÑÑÐ¾Ð»Ñ Ð½Ð°Ð´ блоком ÑÑанзакÑий, оказавÑимÑÑ Ð² пÑеÑванном ÑоÑÑоÑнии из-за оÑибки ÑиÑÑемÑ, не ÑÑиÑÐ°Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑи полноÑÑÑÑ Ð¾ÑмениÑÑ ÐµÑ Ð¸ наÑаÑÑ Ñнова.