SERIALIZABLE — PostgreSQL-də tranzaksiyaların ən yüksək izolyasiya səviyyəsidir. Bu səviyyə zəmanət verir ki, paralel tranzaksiyaların nəticələri elə olacaq, sanki onlar ARDICIL şəkildə, bir-birinin ardınca icra olunub. Bu zaman heç bir paralel icra anomaliyası (məsələn, Dirty Read, Non-Repeatable Read, Phantom Read) baş verə bilməz.
Sadə dillə desək, SERIALIZABLE paralel tranzaksiyalar arasında tam qayda və tutarlılıq yaradır. Elə bil PostgreSQL deyir: "Bütün tranzaksiyalar — növbəyə durun, dostlar!"
Bəs SERIALIZABLE səviyyəsi niyə lazımdır? Bəzən 100% əmin olmaq istəyirsən ki, məlumatların tam tutarlı qalır, paralel dəyişikliklərə baxmayaraq. Təsəvvür elə supermarket səhnəsi, kassirlər eyni anda müştərilərə xidmət edir. Əgər heç kim növbəyə nəzarət etməsəydi, mağazadan çıxanda alınandan çox məhsul ola bilərdi. SERIALIZABLE ilə belə bir şey mümkün deyil.
SERIALIZABLE səviyyəsinin qurulması nümunəsi
SERIALIZABLE izolyasiya səviyyəsini təyin etmək üçün bu əmrdən istifadə edirik:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
Məsələn, bu səviyyədən istifadə edən tranzaksiya yaradaq:
BEGIN; -- Tranzaksiyaya başlayırıq
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; -- İzolyasiya səviyyəsini təyin edirik
SELECT * FROM products WHERE category = 'Electronics'; -- Məhsullar siyahısını alırıq
UPDATE products SET stock = stock - 1 WHERE product_id = 123; -- Qalıqı yeniləyirik
COMMIT; -- Dəyişiklikləri təsdiqləyirik
Case: kinoteatrda bilet bronlamaq
Gəlin real bir nümunəyə baxaq, harada SERIALIZABLE səviyyəsi mütləq lazımdır. Təsəvvür elə, online kinoteatr bilet bronlama sistemi hazırlayırsan. İstifadəçilər yerləri seçir və sən əmin olmaq istəyirsən ki, eyni yer iki müştəriyə eyni anda satılmasın.
Əvvəlcə yerlər üçün cədvəl yaradaq:
CREATE TABLE seats (
seat_id SERIAL PRIMARY KEY,
is_booked BOOLEAN DEFAULT FALSE
);
İndi bir neçə yer əlavə edək:
INSERT INTO seats (is_booked) VALUES (FALSE), (FALSE), (FALSE);
SERIALIZABLE ilə tranzaksiya nümunəsi göstərək.
Yerin təhlükəsiz bronlanmasını belə həyata keçirmək olar:
BEGIN; -- Tranzaksiyanın başlanğıcı
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; -- SERIALIZABLE izolyasiya səviyyəsi
-- Yerin boş olduğunu yoxlayırıq
SELECT is_booked FROM seats WHERE seat_id = 1;
-- Yeri bron edirik
UPDATE seats SET is_booked = TRUE WHERE seat_id = 1;
COMMIT; -- Bronu təsdiqləyirik
Eyni yeri ikinci paralel tranzaksiya bronlamağa çalışanda PostgreSQL heç bir qarışıqlığa imkan verməyəcək və serializasiya konflikti xətası atacaq.
Phantom Read-in qarşısının alınması
İndi isə gəlin "phantom oxunuşlar"ı aydınlaşdıraq, hansılardan ki, qurtulmaq istəyirdik. Phantom Read o zaman baş verir ki, tranzaksiya işləyərkən başqa bir tranzaksiya tərəfindən əlavə olunan məlumat dəyişikliyini görür. Məsələn, tranzaksiyan gözləyir ki, müəyyən sayda sətir olacaq, amma başqa tranzaksiya birdən sətir əlavə edir və ya silir, nəticəni dəyişir.
Nümunəyə baxaq:
Tranzaksiyalardan əvvəlki məlumatlar
| id | balance | user |
|---|---|---|
| 1 | 1000 | Alice |
| 2 | 500 | Bob |
Tranzaksiya 1
BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- Balansı 400-dən çox olan istifadəçilərin sayı
SELECT COUNT(*) FROM accounts WHERE balance > 400;
-- Gözlənilən nəticə: 2 (Alice və Bob)
Tranzaksiya 2
Başqa sessiyada paralel tranzaksiya icra olunur:
BEGIN;
INSERT INTO accounts (id, balance, user) VALUES (3, 700, 'Charlie');
COMMIT;
Tranzaksiya 1-ə qayıdırıq
-- Sorğunu təkrar edirik
SELECT COUNT(*) FROM accounts WHERE balance > 400;
İndi əgər SERIALIZABLE istifadə etməsək, nəticə 2 əvəzinə 3 olacaq, çünki Charlie Tranzaksiya 1 işləyərkən əlavə olunub. Bu, məhz Phantom Read-dir.
Amma SERIALIZABLE ilə PostgreSQL zəmanət verir ki, Tranzaksiya 1 Charlie-ni görməyəcək, çünki onun "dünyagörüşü" tranzaksiyanın başlanğıcında dondurulub.
SERIALIZABLE səviyyəsinin xüsusiyyətləri və məhdudiyyətləri
Artıq başa düşdük ki, SERIALIZABLE ideal izolyasiya verir. Amma bu dünyada nə idealdır ki, qüsursuz olsun? Gəlin açıq danışaq.
Performansın azalması
SERIALIZABLE READ COMMITTED və ya REPEATABLE READ-dən qat-qat çox resurs tələb edir. Niyə? PostgreSQL əməliyyatların ardıcıl icrasını emulyasiya etməli olur, bütün mümkün tranzaksiya konfliktlərini izləyir.
Serializasiya xətaları
Əgər PostgreSQL "ideal ardıcıllıqda" tranzaksiyaları icra edə bilmirsə, serializasiya xətası (serialization_failure) atır və tranzaksiyanı geri qaytarır.
Xəta nümunəsi:
ERROR: could not serialize access due to concurrent update
Belə hallarda, tranzaksiyanı uğursuzluqdan sonra təkrar başlatmaq olar:
DO $$
DECLARE
done BOOLEAN := FALSE;
BEGIN
WHILE NOT done LOOP
BEGIN
-- Tranzaksiyaya başlayırıq
BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- Əməliyyatları icra edirik
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- Dəyişiklikləri təsdiqləyirik
COMMIT;
done := TRUE; -- Hər şey uğurlu olarsa, dövrədən çıxırıq
EXCEPTION WHEN serialization_failure THEN
ROLLBACK; -- Xəta zamanı geri qaytarırıq
END;
END LOOP;
END;
$$;
Bu, SERIALIZABLE istifadə olunan sistemlərdə adi yanaşmadır.
Bu kod PL-SQL istifadə edilərək yazılıb. Sonra ona qayıdacağıq. Sadəcə istədim sizə gözəl və işlək kod göstərim. Həm də göstərim ki, PL-SQL nəyə lazımdır :)
SERIALIZABLE nə vaxt istifadə olunmalıdır?
Bu izolyasiya səviyyəsi o zaman əsaslandırılır ki, səhvin qiyməti çox yüksəkdir:
- Maliyyə tranzaksiyaları, məsələn, ödənişlərin işlənməsi və ya bonusların bölüşdürülməsi.
- Ehtiyat idarəetmə sistemləri, sifarişlərin təkrarlanmasının qarşısını almaq üçün.
- Onlayn bronlama, resursların bronunda konfliktin qarşısını almaq vacibdirsə.
Əgər sistem hazırlayırsan və məlumatların 100% tutarlı olmasını istəyirsən, performans ikinci plandadırsa, SERIALIZABLE sənin ən yaxşı dostun olacaq.
GO TO FULL VERSION