در این مقاله میخوایم در مورد درجه های مختلف انزوا در دیتابیس ها صحبت کنیم. مزیت هایی که درجههای انزوای کمتر برای ما به همراه دارند و مشکلات همزمانی ای که ممکنه با استفاده از این درجههای پایین تر انزوا دچار اونها بشیم. در این مقاله ما بر روی حالتهای انزوای serializable و حالت های انزوای درجه پایین تر و این که این درجههای پایینتر ممکنه نرم افزار ما رو دچار چه مشکلاتی کنن تمرکز میکنیم.
سال هاست که دیتابیس ها به کاربرهای خودشون این امکان رو دادن که از بین درجه های مختلف انزوا (isolation) مورد مناسب خودشون رو از بین «serializability» که بالاترین حد اونه تا «read committed» یا «read uncommitted» که پایین ترین حد اونه انتخاب کنن. هر درجه از این درجههای انزوا امکان داره که نرمافزار رو در معرض مشکلات مشخصی در همزمانی (concurrency bugs) قرار بده. اگرچه اکثر کاربرا به درجهی انزوای پیشفرض هر دیتابیسی که از آن استفاده میکنن اکتفا میکنن و به این مساله توجه نمیکنن که واقعا چه درجه ای از انزوا مناسب نرمافزار اونها است. این راهکار خطرناکه چون خیلی از دیتابیس های محبوب از جمله Oracle, IBM DB2, Microsoft SQL Server, SAP HANA, MySQLو PostgreSQL هیچگونه serializability (حالت ایده آل انزوا در شرایط همزمانی) به صورت پیشفرض ارایه نمیدهند. همانطور که پایینتر توضیح میدیم حالتهای انزوای کمتر از serializability ممکنه به مشکلات همزمانی و ایجاد تجربه کاربری منفی منجر بشه. این خیلی مهمه که کاربر دیتابیس از درجههای انزوایی که دیتابیس برای اون تضمین کرده و مشکلات همزمانی ای که ممکنه با استفاده از هر کدوم از این درجهها ایجاد بشه آگاه باشه.
انزوا در دیتابیس به معنی این قابلیت است که دیتابیس شرایط رو فراهم کنه که یک تراکنش(transaction) به شکلی اجرا بشه که انگار هیچ تراکنش دیگهای به صورت همزمان در حال اجرا نیست(اگرچه ممکنه در واقع تعداد زیادی تراکنش همزمان درحال اجرا وجود داشته باشه). هدف اصلی جلوگیری از نوشته شدن داده های موقت، ناخواسته و غلط توسط تراکنشهای همزمان است.
خوشبختانه چیزی به اسم انزوای ایدهآل وجود داره (پایینتر توضیح میدیم). متاسفانه اما، ایدهآل بودن به قیمت کاهش بازدهی تمام میشه (این کاهش بازدهی ممکنه به صورت افزایش زمان تراکنش -- زیاد شدن زمان لازم برای کامل شدن یک تراکنش -- یا کاهش بازدهی -- تعداد تراکنش در واحد زمان -- ظاهر بشه). سادگی یا پیچیده بودن دستیابی به انزوای ایدهآل در یک سیستم بستگی به معماری اون سیستم داره. در سیستم هایی که معماری ضعیفی دارن دستیابی به انزوای ایدهآل گرون تموم میشه و کاربرها مجبور میشن به یک سری حداقلها اکتفا کنن. اگرچه حتی در سیستم هایی که از معماری خوبی برخوردار هستن اکتفا به یک سری حداقلها افزایش قابل عملکرد سیستم رو به همراه داره. بنابراین درجههای انزوا برای این به وجود اومدن که یک توسعه دهندهها توانایی این رو داشته باشن که یک موازنه بین ضمانت حدودی انزوای تراکنشها و عملکرد قابلقبول سیستم بهوجود بیارن.
بیایید بحثمون رو با این ایده شروع کنیم که اصلا انزوای ایدهآل یعنی چی؟ ما بالاتر انزوا رو اینطور تعریف کردیم که دیتابیس شرایط رو فراهم کنه که یک تراکنش(transaction) به شکلی اجرا بشه که انگار هیچ تراکنش دیگهای به صورت همزمان در حال اجرا نیست(اگرچه ممکنه در واقع تعداد زیادی تراکنش همزمان درحال اجرا وجود داشته باشه). معنی ایدهآل با توجه به این تعریف چیه؟ در اولین نگاه به دست آوردن حالت ایدهآل غیرممکن به نظر میاد. وقتی دو تراکنش به صورت هم زمان روی یک منبع به خصوص مینویسن و میخونن حتما روی هم تاثیر میگذارن. اگه این دو همدیگر رو نادیده بگیرن و روی همون حالت اولیه تغییر بدن هر کدوم که آخر انجام بشه قبلی رو دچار مشکل میکنه، انگار که قبلی هیچ وقت اجرا نشده.
دیتابیس یکی از اولین سیستم های همزمان مقیاس پذیر بود، و به عنوان یک الگوی قبلی برای سیستم های همزمانی که بعدها توسعه داده شدن مورد استفاده قرار گرفت. جامعه توسعه سیستم دیتابیس ها دهها سال قبل یک مکانیزم بسیار قدرتمند برای رویارویی با پیچیدگی های پیاده سازی برنامههای همزمان توسعه داد.
ایده این بود: انسان ها در تحلیل هم زمانی به شکل بنیادی مشکل دارن. این یک برنامهی بدون باگ غیر همزمان بنویسیم به اندازه کافی سخت هست. اما زمانی که شما هم زمانی رو به این معادله اضافه میکنید، تقربیا بینهایت حالت، شرایط رقابت ممکنه ایجاد بشه(اگر یکی از threadها به خط ۱۷ برنامه برسه قبل از این که دیگری به خط ۵ برسه، اما بعد از این که به خط ۳ رسیده، ممکنه مشکلی پیش بیاد که در هیچ حالت دیگه ای از اجرای برنامه امکان به وجود اومدنش وجود نداشته باشه). این تقربیا غیر ممکنه که تمام حالت هایی که قسمت های مختلف اجرای یک برنامه ممکنه با هم تلاقی کنن رو در نظر بگیریم، و این که حالت های مختلف این تلاقی ممکنه منجر به چه نتیجه ای بشه. در عوض دیتابیس ها یک انتزاع زیبا رو برای توسعه دهندهی نرمافزار فراهم میکنن. تراکنش ها! تراکنش ها ممکنه شامل هر تکه کدی باشن اما نکته مهم اینه که اون ها single-thread هستن. توسعه دهندهنرم افزار فقط باید به کد داخل تراکنش دقت کنه (در واقع فقط لازمه چک کنه که وقتی این کد اجرا میشه و هیچ پردازش همزمانی در سیستم وجود نداره کد درست اجرا میشه). دیتابیس در هر وضعیت شروعی باشه اجرا شدن کد نباید نتیجهای خارج از منطق تعریف شدهی برنامه داشته باشه. اگرچه اطمینان از درست بودن کد کار ساده ای نیست، اما اطمینان پیاده کردن از درست بودن اون درحالی که به تنهایی داره اجرا میشه خیلی راحت تر از وقتیه که یک پردازش دیگه داره سعی میکنه منابع مشترک رو دستکاری کنه.
اگر برنامه نویس بتونه اطمینان حاصل کنه که یک تکه کد در حالتی که به تنهایی در حال اجراست درست کار میکنه، انزاوی ایدهآل تضمین میکنه که وقتی کد به صورت همزمان با پردازش های دیگه که ممکنه منابع مشترک رو تغییر بدن در حال اجراست درست کار میکنه. به بیان دیگه، دیتابیس به توسعه دهنده این امکان رو میده که بدون نگرانی در مورد پیچیدگیهای هم زمانی احتمالی کد رو توسعه بده، با این وجود کد بدون این که باگ جدیدی ایجاد بشه یا از منطق برنامه خارج بشه به صورت همزمان اجرا بشه.
پیاده سازی این مرحله از ایدآل بودن به نظر خیلی سخت میاد، اما در واقع سرراست و دست یافتنیه. ما از قبل مطمین شدیم که کد با در نظر گرفتن هر حالت ابتداییای که دیتابیس در اون قرار داره و با این فرض که کد به تنهایی اجرا میشه درست کار میکنه. بنابراین اگر کد تراکنش ها به صورت سریالی اجرا بشه (پشت سر هم) نتیجهی نهایی بازهم درسته. بنابراین، برای این که به انزوای ایدهآل برسیم تمام کاری که باید بکنیم اینه که مطمین بشیم تراکنش ها درست اجرا میشن، حالت نهایی معادل حالتی خواهد بود که اگر تراکنش ها به صورت همزمان اجرا میشدن به وجود میاومد. راه های زیادی برای انجام این کار وجود داره (مثل قفل کردن منابع مشترک، اعتبارسنجی و multi-versioning) که مورد بحث این مطلب نیست. مساله اصلی برای ما اینه که «انزوای ایدهآل» رو به صورت اجرای موازی تراکنش ها در سیستم تعریف میکنیم، اما به شکلی که انگار دارن پشت سر هم اجرا میشن. در استاندارد SQL، این انزوای ایدهآل serializability نامیده میشه.
این داستان ادامه دارد ...