Təsəvvür elə, online oyun oynayırsan, amma bu vaxt hansısa bir çiter oyunun koduna müdaxilə edib öz oyunçusunu gücləndirir, ya da kitabxanada kitab oxuyursan, amma kimsə o arada səhifələri dəyişir, yeni fəsillər əlavə edir, ya da ümumiyyətlə kitabı dəyişir. Qeyri-adi və əsəbi bir vəziyyətdir, düzdür? Bax, məhz belə "sürprizlərdən" səni REPEATABLE READ izolyasiya səviyyəsi qoruyur.
REPEATABLE READ sənə zəmanət verir ki, bir tranzaksiya içində gördüyün datalar tranzaksiya bitənə qədər dəyişməyəcək. Hətta başqa bir tranzaksiya bu dataları yeniləməyə çalışsa belə, sənin tranzaksiyan bu dəyişikliklərdən sığortalanıb.
Açar xüsusiyyətlər:
Dirty Read(hələ təsdiqlənməmiş dataların oxunması) qarşısını alır.- Və ən əsası,
Non-Repeatable Read-in qarşısını alır. Yəni, tranzaksiyanın əvvəlində bir data oxusan, təkrar oxuyanda da eyni datanı görəcəksən, hətta başqa user onu dəyişsə belə.
Amma REPEATABLE READ səni Phantom Read-dən qorumur. Başqa bir tranzaksiya yeni sətrlər əlavə etsə, onlar sənin təkrar sorğunda görünə bilər. Bu anomaliyanı da aradan qaldırmaq üçün SERIALIZABLE səviyyəsi lazımdır, amma bu barədə sonra danışacağıq.
REPEATABLE READ izolyasiya səviyyəsini necə qurmaq olar
Nümunələrə keçməzdən əvvəl, gəlin baxaq bu izolyasiya səviyyəsini PostgreSQL-də necə aktivləşdirmək olar. İki əsas yol var:
Yalnız bir tranzaksiya üçün izolyasiya səviyyəsini təyin et:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; BEGIN; -- Sənin sorğuların COMMIT;Cari sessiya üçün izolyasiya səviyyəsini təyin et:
SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ;
İkinci halda, cari sessiyadakı bütün tranzaksiyalar REPEATABLE READ istifadə edəcək.
Nümunə: Non-Repeatable Read-in qarşısını almaq
Tutaq ki, bizdə belə bir accounts cədvəli var:
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_name TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending'
);
INSERT INTO orders (customer_name, status)
VALUES ('Alice', 'pending'), ('Bob', 'pending');
Ən sadə ssenaridən başlayaq: bir tranzaksiya datanı dəyişir, digəri oxuyur.
REPEATABLE READ olmadan ssenari (READ COMMITTED səviyyəsi)
Tranzaksiya 1 işə başlayır:
BEGIN;
SELECT balance FROM accounts WHERE account_id = 1;
-- Alırıq: 100
Bu vaxt Tranzaksiya 2 datanı dəyişir:
BEGIN;
UPDATE accounts SET balance = 150 WHERE account_id = 1;
COMMIT;
Tranzaksiya 1 davam edir:
SELECT balance FROM accounts WHERE account_id = 1;
-- Alırıq: 150 (data dəyişdi!)
COMMIT;
Gördüyün kimi, READ COMMITTED səviyyəsində bir tranzaksiya içində iki dəfə oxuyanda data dəyişə bilər. Bu da Non-Repeatable Read-dir.
REPEATABLE READ ilə ssenari
İndi eyni nümunəni REPEATABLE READ izolyasiya səviyyəsi ilə yoxlayaq.
Tranzaksiya 1:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
SELECT balance FROM accounts WHERE account_id = 1;
-- Alırıq: 100
Tranzaksiya 2:
BEGIN;
UPDATE accounts SET balance = 150 WHERE account_id = 1;
COMMIT;
Tranzaksiya 1 işə davam edir:
SELECT balance FROM accounts WHERE account_id = 1;
-- Hələ də alırıq: 100 (data dəyişməz qalıb!)
COMMIT;
Başqa tranzaksiya nə dəyişiklik etsə də, tranzaksiya 1 datanı başladığı andakı kimi görür. Beləliklə, Non-Repeatable Read qarşısı alınır.
REPEATABLE READ necə işləyir
PostgreSQL MVCC (Multi-Version Concurrency Control) mexanizmini istifadə edir ki, REPEATABLE READ izolyasiya səviyyəsini reallaşdırsın. MVCC-nin əsas ideyası budur ki, hər tranzaksiya öz sabit "snapshot"-unu alır və bu snapshot tranzaksiya bitənə qədər dəyişmir. Bu, sətrin bir neçə versiyasının yaradılması və idarə olunması ilə əldə olunur.
Tranzaksiya başladığı anda, o, datanı həmin anki vəziyyətdə görür. Başqa tranzaksiya dəyişiklik etsə, PostgreSQL yeni sətr versiyası yaradır, amma əvvəlki versiya hələ də istifadə edən tranzaksiyalar üçün qalır.
Məhz buna görə tranzaksiyalar bəzən yavaş işləyir və çox yaddaş tələb edir. Və məhz buna görə ən güclü izolyasiya səviyyəsində az adam işləyir: ən etibarlısıdır, amma performansı ən çox azaldır.
REPEATABLE READ limitləri: Phantom Read
Dediyimiz kimi, REPEATABLE READ səni Phantom Read-dən qorumur. Bu nə deməkdir, gəlin range ilə işləyən sorğular nümunəsində baxaq.
Tutaq ki, bizdə belə bir orders cədvəli var:
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
amount NUMERIC NOT NULL
);
INSERT INTO orders (amount)
VALUES (50), (100), (150);
Tranzaksiya 1:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
SELECT COUNT(*) FROM orders WHERE amount > 50;
-- Alırıq: 2
Tranzaksiya 2:
BEGIN;
INSERT INTO orders (amount) VALUES (200);
COMMIT;
Tranzaksiya 1 işə davam edir:
SELECT COUNT(*) FROM orders WHERE amount > 50;
-- Alırıq: 3 (yeni sətir sorğunun nəticəsində göründü!)
COMMIT;
Bu halda yeni sətir (amount = 200) başqa tranzaksiya tərəfindən əlavə olundu və "fantom" kimi tranzaksiya 1-in sorğusunda göründü, baxmayaraq ki, izolyasiya səviyyəsi REPEATABLE READ idi.
Əgər Phantom Read-dən qaçmaq istəyirsənsə, SERIALIZABLE səviyyəsini seçməlisən, amma bu həmişə performansla kompromis deməkdir.
REPEATABLE READ-in üstünlükləri və çatışmazlıqları
REPEATABLE READ izolyasiya səviyyəsi əladır, əgər sənə lazımdır ki, datalar tranzaksiya ərzində dəyişməsin. Nə vaxtsa nəyisə oxudunsa, bu dəyər COMMIT-ə qədər dəyişməyəcək, hətta başqa biri başqa tranzaksiyada dəyişiklik etsə belə.
Bu yanaşma həm dirty read (çirkli oxuma), həm də non-repeatable read (təkrarlanmayan oxuma) qarşısını alır. Yəni, başladığın datalarla işləyirsən — heç bir gözlənilməz "on the fly" update yoxdur. Xüsusilə reportlar və ya qərar qəbulunda konsistentlik vacibdirsə, çox faydalıdır.
Digər tərəfdən, REPEATABLE READ "fantom" (phantom read) deyilən hallarla bacarmır — yəni, yeni sətrlər artıq etdiyin sorğunun nəticəsində görünə bilər. Üstəlik, yüksək yük zamanı bu səviyyə tranzaksiyalar arasında konfliktlərə səbəb ola bilər, xüsusilə də eyni dataya tez-tez müraciət olunursa. Bu, bloklanmalara və rollback-lərə gətirib çıxara bilər, hətta sorğular düzgün olsa belə.
Ümumiyyətlə, REPEATABLE READ etibarlılıq və performans arasında yaxşı balansdır, amma yüksək rəqabətli ssenarilərdə əlavə tənzimləmə və diqqət tələb edə bilər.
Faydalı məsləhətlər və tipik səhvlər
- Unutma ki, izolyasiya səviyyəsinin seçimi performansa təsir edir.
REPEATABLE READ-i yalnız datanın dəyişməzliyinə əmin olmaq lazım olanda istifadə et. REPEATABLE READvəSERIALIZABLEarasında qarışıqlıq — çox yayılmış səhvdir. Əgər təkrar sorğuda yeni sətrlər görürsənsə, buREPEATABLE READüçün normaldır.- Uzun tranzaksiyalarla işləyəndə bloklama konfliktlərinə diqqət et. Uzun tranzaksiyalar digər əməliyyatları bloklaya bilər.
PostgreSQL tranzaksiya izolyasiyasını idarə etmək üçün müxtəlif alətlər verir. REPEATABLE READ səviyyəsi, bir tranzaksiya çərçivəsində artıq oxunmuş datanın dəyişməzliyinə əmin olmaq lazım olanda idealdır.
GO TO FULL VERSION