Bəzən transaksiyalar superqəhrəman filmlərinin personajlarını xatırladır. Onlar bazalarımızı nasazlıq, səhv və ya problemlər zamanı fəlakətdən xilas edirlər. Əgər bir neçə əməliyyatın birlikdə icra olunmalı olduğu bir tapşırıqla işləyirsənsə, transaksiyalar onların hamısının bir yerdə baş verməsini təmin edir. Gəlin, bu işin necə işlədiyini ödənişlərin işlənməsi nümunəsində baxaq.
Ödənişlərin işlənməsi
Klassik bir vəziyyəti təsəvvür et: iki bank hesabın var və birindən digərinə pul köçürmək istəyirik. Bu, sadəcə "bir düymə" əməliyyatı deyil. Əmin olmalıyıq ki, bir hesabdan düzgün məbləğ çıxılıb və digər hesaba əlavə olunub. Hər hansı bir səhv fəlakətə səbəb ola bilər: ya hər iki hesab toxunulmaz qalacaq, ya da balans pozulacaq (məsələn, pul yoxa çıxacaq və ya "havadan" peyda olacaq).
Ssenari: Hesablar arasında pul köçürməsi
Budur kodumuz. Diqqətlə oxu, sanki uzaq bir qalaktikadan gələn mesajdır:
-- Transaksiyaya başlayırıq
BEGIN;
-- Addım 1. Göndəricinin hesabından pul çıxırıq
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;
-- Addım 2. Alıcının hesabına pul əlavə edirik
UPDATE accounts
SET balance = balance + 100
WHERE account_id = 2;
-- Hər şey yaxşı getdi? Onda dəyişiklikləri saxlayırıq!
COMMIT;
Burada nə vacibdir?
Addım 1və yaAddım 2-də nəsə səhv getsə (məsələn, sorğuda səhv, vəsait çatışmazlığı), transaksiyanı geri qaytarmaq üçünROLLBACKistifadə oluna bilər və datalar əvvəlki vəziyyətdə qalacaq.COMMITtəmin edir ki, dəyişikliklər yalnız BÜTÜN addımlar uğurlu olarsa tətbiq olunur.
Balans yoxlaması əlavə edirik
Bəs göndəricinin köçürmə üçün kifayət qədər vəsaiti yoxdursa? Gəlin balans yoxlaması əlavə edək ki, təsadüfən "minus"a düşməsin.
-- Transaksiyaya başlayırıq
BEGIN;
-- Göndəricinin cari balansını alırıq
DO $$
DECLARE
current_balance NUMERIC;
BEGIN
SELECT balance INTO current_balance FROM accounts WHERE account_id = 1;
-- Yoxlayırıq, pul kifayət edirmi
IF current_balance >= 100 THEN
-- Pul kifayətdirsə, köçürməni edirik
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;
UPDATE accounts
SET balance = balance + 100
WHERE account_id = 2;
-- Dəyişiklikləri təsdiqləyirik
COMMIT;
ELSE
-- Pul kifayət etmirsə, geri qaytarırıq
ROLLBACK;
RAISE NOTICE 'Köçürmə üçün vəsait kifayət deyil!';
END IF;
END $$;
Burada nə maraqlıdır?
- PL/pgSQL blokunda
IFilə şərt yoxlaması istifadə edirik. Əgər balans lazım olan məbləğdən azdırsa, transaksiya rədd olunur və heç nə dəyişmir. ROLLBACKdəyişiklikləri ləğv edir, əgər başlanıbsa (bu halda hələ ləğv olunacaq bir şey yoxdur, amma yaxşı vərdişdir).
Bu mühazirə transaksiyaların real ssenarilərdə istifadəsinə həsr olunub, ona görə də burada real həyatdan bir nümunə gətirdim. Burada saxlanılan prosedur var və PL-SQL ilə yazılıb. Düşünürəm ki, artıq kifayət qədər təcrübəniz var ki, burada hər şeyin necə işlədiyini başa düşəsiniz. Gələcəkdə PL-SQL mövzusuna qayıdacağıq və daha mürəkkəb nümunələrə baxacağıq.
Transaksiyada kütləvi data yenilənməsi
Transaksiya yaratmaq təkcə pul köçürmələri üçün deyil, başqa tapşırıqlarda da faydalıdır. Tutaq ki, internet mağazası bazamız var və hər gün onlarla sifarişin statusu dəyişə bilər, məsələn, "çatdırılır"dan "tamamlandı"ya. Bir çox yazını birdən necə yeniləyək ki, nasazlıq olarsa, dəyişiklikləri geri qaytarmaq mümkün olsun? Əlbəttə, transaksiyadan istifadə etməklə.
Başqa bir ssenariyə baxaq: sifariş statuslarının yenilənməsi.
Budur nümunə:
-- Transaksiyaya başlayırıq
BEGIN;
-- Addım 1. Çatdırılma tarixi keçmiş sifarişləri yeniləyirik
UPDATE orders
SET status = 'tamamlandı'
WHERE delivery_date < CURRENT_DATE;
-- Addım 2. Uğurlu yenilənmə barədə bildiriş veririk
RAISE NOTICE 'Bütün sifariş statusları uğurla yeniləndi.';
-- Dəyişiklikləri tətbiq edirik
COMMIT;
Bəs nəsə səhv getsə?
Həmişə səhv ehtimalı var. Məsələn, təsadüfən WHERE şərtini unutdun və indi bütün sifarişlərin statusu tamamlandı oldu. Belə hallardan qaçmaq üçün transaksiyanı bitirmək və ya açıq şəkildə geri qaytarmaq vacibdir.
Geri qaytarma ssenarisinə baxaq:
-- Transaksiyaya başlayırıq
BEGIN;
-- Addım 1. Sifarişləri şərtsiz yeniləməyə cəhd (ay, səhv!)
UPDATE orders
SET status = 'tamamlandı';
-- Səhv səbəbindən transaksiyanı geri qaytarırıq
ROLLBACK;
-- İndi sifarişlər dəyişməz qaldı
SAVEPOINT ilə bir az "çeviklik" əlavə edirik
Həmişə bütün transaksiyanı geri qaytarmaq lazım olmur. Əgər ssenariniz bir neçə addımdan ibarətdirsə, bəzən yalnız bir hissəni geri qaytarmaq lazım olur. Burada SAVEPOINT köməyə gəlir.
İndi ssenarimiz belədir: bir neçə addımın işlənməsi və onlardan birini geri qaytarmaq imkanı.
Təsəvvür et ki, bir sifarişi bir neçə addımda işləyirsən: anbardan məhsulların çıxılması, sifariş statusunun yenilənməsi, müştəriyə bildiriş göndərilməsi. Əgər bildiriş uğurla göndərilmədisə, yalnız bu addımı geri qaytarmaq istəyirsən, amma bazadakı dəyişiklikləri saxlamaq istəyirsən.
-- Transaksiyaya başla
BEGIN;
-- Addım 1. Məhsulları anbardan çıxırıq
UPDATE products
SET stock = stock - 1
WHERE product_id = 101;
-- Geri qaytarma nöqtəsini saxlayırıq
SAVEPOINT step1;
-- Addım 2. Sifariş statusunu yeniləyirik
UPDATE orders
SET status = 'göndərildi'
WHERE order_id = 202;
-- Müştəriyə bildiriş göndərməyə cəhd
SAVEPOINT step2;
-- Ay, bildiriş prosesində səhv!
ROLLBACK TO SAVEPOINT step2;
-- Qərara gəlirik ki, transaksiyanı bitirmək təhlükəsizdir
COMMIT;
Nəticə
Transaksiyalar — sadəcə texniki alət deyil, datalarınızın bütövlüyünə zəmanətdir. Onlar "domino effekti"ndən qoruyur, yəni bir səhv bütün sistemi dağıda bilər. Hər dəfə bir neçə əlaqəli əməliyyat edəndə özündən soruş: "Bəs biri uğursuz olsa nə olar?" Əgər cavab "fəlakət"dirsə, deməli, transaksiyadan istifadə vaxtıdır. Unutma: transaksiyanı yazmağa bir neçə dəqiqə sərf etmək, nasazlıqdan sonra dataları bərpa etməyə saatlar sərf etməkdən yaxşıdır. İstifadəçilərin (və əsəblərin) sənə təşəkkür edəcək!
GO TO FULL VERSION