Isolation Levels — READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE

~25 daq · MySQL · Transaction va Qulflar

Muammodan boshlaylik

Ikki tranzaksiya bir vaqtda ishlayapti:

  • A — buxgalter oylik hisobotni tuzyapti: 50 ta SELECT, 2 daqiqa davom etadi
  • B — shu paytda mijozlar buyurtma berib, narxlar yangilanmoqda

Savol: A tranzaksiyasi B ning o'zgarishlarini ko'rishi kerakmi? Agar ko'rsa — hisobotning boshi va oxiri turli holatdagi ma'lumotga asoslanadi: jami summa qatorlar yig'indisiga teng chiqmaydi. Agar ko'rmasa — qanday qilib?

Isolation darajasi — tranzaksiyalar bir-birining o'zgarishlarini qanchalik "ko'rishi" ni belgilaydi. Bu tezlik va aniqlik o'rtasidagi tanlov.


Uch xil anomaliya

Isolation yetarli bo'lmaganda paydo bo'ladigan muammolar.

1. Dirty read — "iflos" o'qish

Boshqa tranzaksiyaning hali COMMIT qilinmagan o'zgarishini o'qish:

B: START TRANSACTION
B: UPDATE mahsulotlar SET narx = 100 WHERE id = 1
A:                                    SELECT narx FROM mahsulotlar WHERE id = 1   → 100 ⚠️
B: ROLLBACK                           (narx yana 12 500 000)
A: (100 so'mlik narx bilan buyurtma yaratdi — hech qachon mavjud bo'lmagan qiymat)

2. Non-repeatable read — takrorlanmaydigan o'qish

Bir tranzaksiya ichida bir xil qatorni ikki marta o'qib, turli natija olish:

A: START TRANSACTION
A: SELECT soni FROM mahsulotlar WHERE id = 1    → 4
B: UPDATE mahsulotlar SET soni = 0 WHERE id = 1; COMMIT
A: SELECT soni FROM mahsulotlar WHERE id = 1    → 0 ⚠️ — o'sha tranzaksiyada!

3. Phantom read — "arvoh" qatorlar

Bir xil shart bilan ikki marta so'rab, turli qatorlar to'plamini olish:

A: SELECT COUNT(*) FROM buyurtmalar WHERE holat = 'yangi'   → 10
B: INSERT INTO buyurtmalar (holat) VALUES ('yangi'); COMMIT
A: SELECT * FROM buyurtmalar WHERE holat = 'yangi'          → 11 qator ⚠️

Hisobotda: "jami 10 ta", lekin ro'yxatda 11 ta.


To'rt daraja

Daraja Dirty read Non-repeatable Phantom Tezlik
READ UNCOMMITTED ⚠️ Bor ⚠️ Bor ⚠️ Bor Eng tez
READ COMMITTED ✅ Yo'q ⚠️ Bor ⚠️ Bor Tez
REPEATABLE READ (MySQL standarti) ✅ Yo'q ✅ Yo'q ✅ Deyarli yo'q* O'rtacha
SERIALIZABLE ✅ Yo'q ✅ Yo'q ✅ Yo'q Eng sekin

* InnoDB ning REPEATABLE READ i oddiy SELECT lar uchun phantom larni ham oldini oladi (pastda).

Darajani ko'rish va o'zgartirish

SELECT @@transaction_isolation;   -- REPEATABLE-READ

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;          -- shu ulanish uchun
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;                    -- faqat keyingi tranzaksiya

READ UNCOMMITTED

Boshqalarning commit qilinmagan o'zgarishlari ko'rinadi. Deyarli hech qachon ishlatilmaydi — faqat juda taxminiy statistika uchun (masalan, "taxminan nechta qator") va aniqlik umuman muhim bo'lmaganda.


READ COMMITTED

Har bir SELECT — o'sha lahzadagi commit qilingan ma'lumotni ko'radi. Tranzaksiya davomida natijalar o'zgarishi mumkin.

PostgreSQL, Oracle, SQL Server ning standart darajasi. Ko'p veb-ilovalar uchun yetarli: har bir so'rov eng yangi ma'lumotni oladi.


REPEATABLE READ — MySQL standarti

Tranzaksiyadagi birinchi SELECT paytida ma'lumotning "suratga olinishi" (snapshot) yaratiladi. Tranzaksiya oxirigacha barcha SELECT lar shu suratni ko'radi — boshqalar nima commit qilishidan qat'i nazar.

A: START TRANSACTION
A: SELECT soni FROM mahsulotlar WHERE id = 1    → 4   (surat olindi)
B: UPDATE mahsulotlar SET soni = 0 WHERE id = 1; COMMIT
A: SELECT soni FROM mahsulotlar WHERE id = 1    → 4   ✅ (surat)
A: COMMIT
A: SELECT soni FROM mahsulotlar WHERE id = 1    → 0   (yangi tranzaksiya — yangi surat)

Buxgalterning hisoboti uchun ideal: 2 daqiqa davomida u bir xil, izchil ma'lumotni ko'radi.

MVCC — qanday ishlaydi

InnoDB eski ma'lumotni darhol ustidan yozmaydi. Qator o'zgarganda eski versiyasi undo log da saqlanadi. Har bir tranzaksiya o'zining surati vaqtiga mos versiyani o'qiydi.

Bu MVCC (Multi-Version Concurrency Control). Asosiy foyda: o'quvchilar yozuvchilarni bloklamaydi, yozuvchilar o'quvchilarni bloklamaydi. Hisobot 2 daqiqa o'qiyotganda ham buyurtmalar bemalol yoziladi.

Muhim tuzoq: o'qish va yozish farqi

Surat faqat oddiy SELECT ga taalluqli. UPDATE, DELETE va qulflovchi o'qishlar (SELECT ... FOR UPDATE) doim eng yangi commit qilingan ma'lumot bilan ishlaydi:

A: START TRANSACTION
A: SELECT soni FROM mahsulotlar WHERE id = 1    → 4  (surat)
B: UPDATE mahsulotlar SET soni = 1 WHERE id = 1; COMMIT
A: UPDATE mahsulotlar SET soni = soni - 1 WHERE id = 1
   -- haqiqiy qiymat 1 dan ayiradi → 0  (4 dan emas!)
A: SELECT soni FROM mahsulotlar WHERE id = 1    → 0  (o'z o'zgarishini ko'radi)

Shuning uchun "o'qib, ilovada hisoblab, yozish" naqshi xavfli (12-dars):

❌ A: SELECT soni ... → 4 (surat)
   A: (ilovada: 4 - 1 = 3)
   A: UPDATE ... SET soni = 3        ← B ning o'zgarishi yo'qoldi!

✅ A: UPDATE ... SET soni = soni - 1 WHERE soni >= 1
✅ A: SELECT soni ... FOR UPDATE     ← eng yangi qiymat + qulf (38-dars)

SERIALIZABLE

Eng qat'iy daraja: tranzaksiyalar go'yo birin-ketin bajarilgandek natija beradi. InnoDB buning uchun oddiy SELECT larni ham avtomatik SELECT ... FOR SHARE ga aylantiradi — ya'ni o'qilgan qatorlar qulflanadi.

Natija: anomaliyalar umuman yo'q, lekin ko'p kutish va deadlock lar (38-dars). Faqat juda muhim, kam takrorlanadigan amallar uchun.


Qaysi darajani tanlash

Vaziyat Daraja
Oddiy veb-ilova, CRUD REPEATABLE READ (standart) yoki READ COMMITTED
Uzoq hisobotlar, eksport — izchil surat kerak REPEATABLE READ
Yuqori parallellik, kamroq qulf, eng yangi ma'lumot kerak READ COMMITTED
Moliyaviy hisob-kitob, qat'iy to'g'rilik Kerakli joyda SELECT ... FOR UPDATE yoki SERIALIZABLE
Taxminiy statistika READ UNCOMMITTED (kamdan-kam)

Ko'p hollarda darajani o'zgartirish shart emas. Muhimi — xavfli naqshlardan qochish (o'qib-keyin-yozish) va kerakli joyda aniq qulflash (FOR UPDATE).


Nima uchun shunday?

Nima uchun eng xavfsiz daraja (SERIALIZABLE) standart emas?

Chunki xavfsizlikning narxi — parallellik. SERIALIZABLE da hisobot o'qigan har bir qator qulflanadi va shu 2 daqiqa davomida hech kim u qatorlarni o'zgartira olmaydi — sayt "qotib" qoladi. Real tizimlarda bir vaqtda yuzlab tranzaksiya ishlaydi. Har bir baza o'zining "oltin o'rtasini" tanlagan: MySQL — REPEATABLE READ (MVCC tufayli o'quvchilar baribir bloklanmaydi), PostgreSQL — READ COMMITTED. Ikkalasi ham amalda yaxshi ishlaydi, agar dasturchi anomaliyalarni bilsa.


Xulosa

Eslab qoling:

  • Uch anomaliya: dirty read, non-repeatable read, phantom read
  • To'rt daraja: READ UNCOMMITTED < READ COMMITTED < REPEATABLE READ < SERIALIZABLE
  • MySQL standarti — REPEATABLE READ: tranzaksiya bitta "surat"ni ko'radi
  • MVCC — o'quvchilar va yozuvchilar bir-birini bloklamaydi
  • Surat faqat oddiy SELECT uchun; UPDATE va FOR UPDATE — eng yangi ma'lumot bilan
  • "O'qib, ilovada hisoblab, yozish" — xavfli; atomar UPDATE yoki FOR UPDATE
  • SET SESSION TRANSACTION ISOLATION LEVEL ...

Keyingi dars

38-dars: Locking — Row lock, Table lock, Deadlock va yechimi