Isolation Level
این مشکلات در دیتابیس وقتی اتفاق میافتند که تراکنشهای همزمان روی دادههای یکسان تأثیر بگذارند و ایزولهسازی به اندازه کافی قوی نباشد.
🛑 اDirty Read (خواندن کثیف)
🔹 زمانی اتفاق میافتد که یک تراکنش دادهای را بخواند که هنوز در یک تراکنش دیگر Commit نشده است. اگر تراکنش دوم Rollback شود، دادهای که تراکنش اول خوانده بود دیگر معتبر نخواهد بود.
✅ مثال:
- تراکنش
T1مقدارbalanceرا از 100 به 200 تغییر میدهد ولی هنوزCOMMITنکرده است. - تراکنش
T2مقدارbalanceرا بهعنوان 200 میخواند. - تراکنش
T1عملیات راROLLBACKمیکند و مقدارbalanceبه 100 برمیگردد. - حالا تراکنش
T2مقداری خوانده که هیچوقت تأیید نشده بود!
📌 راهحل: سطح ایزولهسازی Read Committed یا بالاتر این مشکل را حل میکند.
🔁 اNon-repeatable Read (خواندن غیرقابل تکرار)
🔹 زمانی رخ میدهد که یک تراکنش دو بار یک داده را بخواند، ولی بین این دو خواندن، تراکنش دیگری مقدار را تغییر دهد. این باعث میشود که مقدار خواندهشده در هر بار متفاوت باشد.
✅ مثال:
- تراکنش
T1مقدارbalanceرا میخواند (مثلاً 100). - تراکنش
T2مقدارbalanceرا به 200 تغییر میدهد وCOMMITمیکند. - دوباره تراکنش
T1مقدارbalanceرا میخواند و این بار 200 است! 📌 راهحل: تنها سطح ایزولهسازی Serializable میتواند از Phantom Read جلوگیری کند. معمولا کوئری دوما در قالب یک aggergate function هستش
begin tx1,tx2
select PID from sales
// tx2 finished
select sum (qnt) from sales
👻ا Phantom Read
🔹 وقتی یک تراکنش چندین بار یک مجموعهای از دادهها را بخواند، اما بین دو خواندن، تراکنش دیگری سطری را اضافه یا حذف کند. در نتیجه، تراکنش اول نتایج مختلفی دریافت میکند.
✅ مثال:
- تراکنش
T1همه کاربرانی کهage > 18دارند را میخواند (مثلاً 5 نفر). - همزمان تراکنش
T2یک کاربر جدید باage = 25اضافه میکند وCOMMITمیکند. - تراکنش
T1دوباره همان کوئری را اجرا میکند، اما این بار 6 کاربر برمیگردد!
📌 راهحل: تنها سطح ایزولهسازی Serializable میتواند از Phantom Read جلوگیری کند. البته توی پست گرس همون سطح repeatable read هم جلوش رو میتونه بگیره چون در ابتدای tx یه اسنپ شات میگیره
😥 ا Lost Update
🔹ا Lost Update زمانی رخ میدهد که دو تراکنش تقریباً همزمان مقدار یک داده را بخوانند، مقدار جدیدی محاسبه کنند، و آن را بنویسند. اما مقدار نوشته شده توسط تراکنش اول، بدون توجه به تغییراتش، توسط تراکنش دوم بازنویسی میشود.
✅ مثال:
- تراکنش
T1مقدارbalanceیک حساب بانکی را میخواند (balance = 1000). - تراکنش
T2همزمان مقدارbalanceرا میخواند (balance = 1000). 3.اT1مبلغ 200 را اضافه میکند و مقدار جدید (balance = 1200) را ذخیره میکند. - ا**
T2** مبلغ 300 را اضافه میکند، اما هنوز مقدار قدیمی (1000) را دارد، پس مقدار جدید را 1300 محاسبه کرده و ذخیره میکند. - در نتیجه، تغییر
T1(یعنی افزایش 200) از بین میرود و مقدار نهایی 1300 خواهد بود، در حالی که باید 1500 میشد! 😨
🔹 انواع سطوح ایزولهسازی:
1️⃣ اRead Uncommitted (خواندن تغییرات تأییدنشده)
- پایینترین سطح ایزولهسازی
- یک تراکنش میتواند دادههای تراکنشهای تأییدنشده (Uncommitted) دیگر را بخواند
- احتمال بروز Dirty Read، Non-repeatable Read و Phantom Read بالا است
- سریع اما ناامن
2️⃣ اRead Committed (خواندن تغییرات تأییدشده)
- تراکنش فقط دادههای تأییدشده را میخواند
- از Dirty Read جلوگیری میکند اما Non-repeatable Read و Phantom Read ممکن است اتفاق بیفتند
- در اکثر دیتابیسها (مثل PostgreSQL و SQL Server) بهعنوان سطح پیشفرض استفاده میشه
3️⃣ اRepeatable Read
- یک تراکنش تا پایان اجرا، دادههایی که خوانده را قفل میکند (تغییرات توسط دیگر تراکنشها مجاز نیست)
- از Dirty Read و Non-repeatable Read جلوگیری میکند اما Phantom Read ممکن است رخ دهد
- در MySQL (با InnoDB) این سطح پیشفرض است
- توی پست گرس چون توی این سطح ایزوله سازی اول tx یه snapshot از db گرفته میشه بخاطر همین فانتوم رید هم با همین سطح قابل حله
4️⃣ اSerializable
- بالاترین سطح ایزولهسازی
- تمام تراکنشها بهصورت سریالی اجرا میشوند (یا از طریق قفلگذاری یا کنترل نسخه)
- از Dirty Read، Non-repeatable Read و Phantom Read جلوگیری میکند
- امنیت بالا اما کارایی کمتر (Concurrency کاهش پیدا میکند)
در PostgreSQL و MySQL میتوان سطح ایزولهسازی را با دستور زیر تنظیم کرد:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
یا
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;