يعني إيه Transaction؟
أي عملية حقيقية في الأبلكيشن عبارة عن كذا تعديل في الداتابيز لازم يحصلوا مع بعض أو ميحصلش ولا واحد. الدرس ده بيعرّف الترانزاكشن، ويوريك هي بتبدأ وتخلص إزاي، والحالات اللي بتمر بيها، والفرق الكبير بين PostgreSQL و MySQL لما Error يطلع في النص.
- تعرّف الـ Transaction وتعرف إمتى لازم تستخدمها
- تستخدم
BEGINوCOMMITوROLLBACKوتفهم الـ Autocommit - تعرف حالات الترانزاكشن من أول ما تبدأ لحد ما تخلص
- تفهم بيحصل إيه لما Error يطلع في النص في PostgreSQL وفي MySQL
1. المشكلة
عميل اشترى من المتجر. العملية دي في الداتابيز عبارة عن 4 حاجات:
INSERT INTO orders (id, user_id, total) VALUES (501, 7, 1200);
INSERT INTO order_items (order_id, product_id, qty) VALUES (501, 33, 1);
UPDATE products SET stock = stock - 1 WHERE id = 33;
UPDATE wallets SET balance = balance - 1200 WHERE user_id = 7;لو السيرفر وقع بعد الجملة التالتة: الأوردر اتعمل، والمخزون نقص، والفلوس ماتخصمتش. ولو الجملة الرابعة فشلت لأن الرصيد مش كفاية: الأوردر موجود والمخزون نقص على الفاضي. وحتى من غير ما حاجة تقع، لو حد قرا الداتا بين الجملة الأولى والرابعة، هيشوف أوردر “مدفوع” والفلوس لسه في المحفظة.
المطلوب إن الأربع جمل دول يبقوا عملية واحدة من وجهة نظر أي حد: يا حصلوا كلهم، يا ولا واحد فيهم حصل.
2. التعريف
BEGIN; -- أو START TRANSACTION
INSERT INTO orders (id, user_id, total) VALUES (501, 7, 1200);
INSERT INTO order_items (order_id, product_id, qty) VALUES (501, 33, 1);
UPDATE products SET stock = stock - 1 WHERE id = 33;
UPDATE wallets SET balance = balance - 1200 WHERE user_id = 7;
COMMIT; -- كله اتثبت مرة واحدة
-- أو ROLLBACK; -- كله اتلغىالقاعدة البسيطة: أي تعديلين لازم يحصلوا مع بعض، حطهم في ترانزاكشن واحدة. ولو جملة واحدة بس، الداتابيز بتعملها ترانزاكشن لوحدها أصلاً (وهو ده الـ Autocommit).
الترانزاكشن بتخلّي كذا تعديل يبانوا للعالم كأنهم تعديل واحد: يا كله يا مفيش.
3. الـ Autocommit
يعني لو بعت الأربع جمل اللي فوق من غير BEGIN، دول 4 ترانزاكشنز منفصلين، وكل واحدة بتتثبت لوحدها. وده أشهر سبب للداتا اللي “نص متسجلة” في الأبلكيشنز.
| MySQL | PostgreSQL | |
|---|---|---|
| الافتراضي | autocommit = 1 |
كل جملة ترانزاكشن لوحدها |
| تبدأ ترانزاكشن | START TRANSACTION أو BEGIN |
BEGIN أو START TRANSACTION |
| تقفل الـ Autocommit للـ Session | SET autocommit = 0 (أي جملة بعدها بتفتح ترانزاكشن لحد ما تعمل COMMIT) |
مفيش إعداد على السيرفر، بيتعمل في الـ Client (زي \set AUTOCOMMIT off في psql) |
4. حالات الترانزاكشن
| الحالة | معناها |
|---|---|
| Active | شغالة: بتقرا وبتكتب |
| Partially Committed | آخر جملة خلصت، وطلبت COMMIT، والداتابيز بتكتب الـ Log على الديسك |
| Committed | الـ Log اتكتب على الديسك، والتعديلات مضمونة ومحدش يقدر يلغيها |
| Failed | حصل Error، ومينفعش تكمّل |
| Aborted | اتعملها ROLLBACK، وكل أثرها اتشال |
اللحظة الفاصلة هي الانتقال من Partially Committed لـ Committed: بيحصل لما سجل الـ COMMIT يتكتب في الـ Log على الديسك (ده درس الـ Durability). قبلها، لو الكهربا قطعت، الترانزاكشن كأنها ماحصلتش. بعدها، حتى لو الكهربا قطعت، التعديلات موجودة.
5. لما Error يطلع في النص
هنا فيه فرق كبير جداً بين الداتابيزين، وبيعمل Bugs حقيقية لو مش واخد بالك منه:
BEGIN; INSERT INTO orders (id, user_id) VALUES (501, 7);INSERT 0 11INSERT INTO orders (id, user_id) VALUES (501, 8);ERROR: duplicate key2UPDATE products SET stock = stock - 1 WHERE id = 33;ERROR: current transaction is aborted3COMMIT;ROLLBACK4- 5
SELECT count(*) FROM orders;0
START TRANSACTION; INSERT INTO orders (id, user_id) VALUES (501, 7);1 row1INSERT INTO orders (id, user_id) VALUES (501, 8);ERROR 1062: Duplicate entry2UPDATE products SET stock = stock - 1 WHERE id = 33;1 row3COMMIT;OK4- 5
SELECT count(*) FROM orders;1 · والمخزون 4
دوس ▶ شغّل، أو امشي خطوة خطوة بالأسهم.
استثناءات مهمة في MySQL: الـ Deadlock بيلغي الترانزاكشن كلها (مش الجملة بس). أما الـ Lock wait timeout فبيلغي الجملة بس (إلا لو innodb_rollback_on_timeout = ON).
الخلاصة العملية: الكود لازم يتعامل مع أي Error جوه ترانزاكشن على إنه “الغي كل حاجة”. ده بالظبط اللي DB::transaction في Laravel بيعمله: أي Exception يطلع بيعمل ROLLBACK للترانزاكشن كلها، في الداتابيزين.
في PostgreSQL أي Error بيقفل الترانزاكشن لحد الـ ROLLBACK. في MySQL الجملة بس اللي بتتلغى والباقي ممكن يتثبت. اكتب كودك على إن أي Error = ROLLBACK كامل.
6. أسئلة الإنترفيو
فكّر في إجابتك الأول، وبعدين افتح السؤال.
01يعني إيه Transaction؟ وإمتى تستخدمها؟
مجموعة عمليات بتتعامل كوحدة واحدة: يا كلها تتثبت بـ COMMIT، يا كلها تتلغى بـ ROLLBACK، ومحدش يشوف نتايجها قبل الـ COMMIT. بتستخدمها لما كذا تعديل لازم يحصلوا مع بعض، زي أوردر وخصم مخزون وخصم رصيد.
02يعني إيه Autocommit؟ وإيه خطره؟
الوضع الافتراضي: كل جملة من غير BEGIN بتبقى ترانزاكشن لوحدها وبتتثبت أوتوماتيك. خطره إنك تبعت كذا جملة مرتبطة من غير BEGIN فيبقوا ترانزاكشنز منفصلين، ولو حاجة وقعت في النص الداتا تبقى نصها متسجل.
03إيه اللي بيحصل في PostgreSQL لو جملة فشلت جوه ترانزاكشن؟ وفي MySQL؟
PostgreSQL: الترانزاكشن كلها بتدخل حالة aborted، وأي جملة بعدها ترفض، والـ COMMIT بيتحول لـ ROLLBACK. MySQL: الجملة اللي فشلت بس اللي بتتلغى، والترانزاكشن بتكمل، والـ COMMIT بيثبت الجمل اللي نجحت. ما عدا الـ Deadlock اللي بيلغي الترانزاكشن كلها.
04إمتى الترانزاكشن تعتبر Committed فعلاً؟
لما سجل الـ COMMIT يتكتب في الـ Log (WAL أو Redo Log) على الديسك. قبلها لو السيرفر وقع كأنها ماحصلتش، وبعدها التعديلات مضمونة حتى لو الكهربا قطعت.