توسعه دهندگان بیشتری باید این را در مورد پایگاه داده بدانند

توجه داشته باشید. ترجمه: جانا دوگان یک مهندس باتجربه در گوگل است که در حال حاضر روی قابلیت مشاهده خدمات تولیدی این شرکت نوشته شده در Go کار می کند. در این مقاله که محبوبیت زیادی در بین مخاطبان انگلیسی زبان به دست آورد، او در 17 نکته جزئیات فنی مهمی را در مورد DBMS ها (و گاهی اوقات سیستم های توزیع شده به طور کلی) جمع آوری کرد که برای توسعه دهندگان برنامه های کاربردی بزرگ/مطلوب مفید است.

توسعه دهندگان بیشتری باید این را در مورد پایگاه داده بدانند

اکثریت قریب به اتفاق سیستم های کامپیوتری وضعیت خود را پیگیری می کنند و بر این اساس به نوعی سیستم ذخیره سازی داده نیاز دارند. من دانشی را در مورد پایگاه‌های داده در مدت زمان طولانی جمع‌آوری کردم، در طول مسیر اشتباهاتی در طراحی انجام دادم که منجر به از دست رفتن داده‌ها و قطع شدن آن شد. در سیستم هایی که حجم زیادی از اطلاعات را پردازش می کنند، پایگاه های داده در قلب معماری سیستم قرار دارند و به عنوان یک عنصر کلیدی در انتخاب راه حل بهینه عمل می کنند. علیرغم این واقعیت که توجه زیادی به کار پایگاه داده می شود، مشکلاتی که توسعه دهندگان برنامه سعی می کنند پیش بینی کنند اغلب فقط نوک کوه یخ هستند. در این سری از مقالات، ایده هایی را به اشتراک می گذارم که برای توسعه دهندگانی که در این زمینه تخصص ندارند مفید خواهد بود.

  1. اگر 99,999٪ مواقع شبکه مشکلی ایجاد نمی کند، شما خوش شانس هستید.
  2. ACID به معنای چیزهای مختلف است.
  3. هر پایگاه داده مکانیزم های خاص خود را برای اطمینان از سازگاری و انزوا دارد.
  4. انسداد خوش بینانه زمانی به کمک می آید که حفظ حالت معمول دشوار باشد.
  5. غیر از خواندن کثیف و از دست دادن اطلاعات، ناهنجاری های دیگری نیز وجود دارد.
  6. پایگاه داده و کاربر همیشه در مورد مسیر عمل توافق ندارند.
  7. اشتراک گذاری در سطح برنامه را می توان به خارج از برنامه منتقل کرد.
  8. افزایش خودکار می تواند خطرناک باشد.
  9. داده های قدیمی می توانند مفید باشند و نیازی به قفل شدن ندارند.
  10. تحریف ها برای هر منبع زمانی معمول هستند.
  11. تاخیر معانی زیادی دارد.
  12. الزامات عملکرد باید برای یک معامله خاص ارزیابی شود.
  13. تراکنش های تو در تو می توانند خطرناک باشند.
  14. تراکنش ها نباید به وضعیت برنامه مرتبط باشند.
  15. برنامه ریزان پرس و جو می توانند چیزهای زیادی در مورد پایگاه داده به شما بگویند.
  16. مهاجرت آنلاین دشوار است، اما ممکن است.
  17. افزایش قابل توجه در پایگاه داده مستلزم افزایش غیرقابل پیش بینی بودن است.

مایلم از امانوئل اودکه، راین هنریکس و دیگران برای بازخوردشان در مورد نسخه قبلی این مقاله تشکر کنم.

اگر 99,999٪ مواقع شبکه مشکلی ایجاد نمی کند، شما خوش شانس هستید.

این سوال باقی می ماند که فناوری های شبکه مدرن چقدر قابل اعتماد هستند و سیستم ها چقدر به دلیل خرابی شبکه از کار می افتند. اطلاعات در مورد این موضوع کمیاب است و تحقیقات اغلب تحت سلطه سازمان های بزرگ با شبکه ها، تجهیزات و پرسنل تخصصی است.

با نرخ در دسترس بودن 99,999٪ برای Spanner (پایگاه داده جهانی توزیع شده گوگل)، گوگل ادعا می کند که فقط ٪۱۰۰ مشکلات مربوط به شبکه است در همان زمان، این شرکت شبکه تخصصی خود را "ستون اصلی" دسترسی بالا می نامد. مطالعه بیلیس و کینگزبری، که در سال 2014 انجام شد، یکی از «تصورات غلط در مورد محاسبات توزیع شده"، که پیتر دویچ در سال 1994 فرموله کرد. آیا شبکه واقعا قابل اعتماد است؟

تحقیقات جامع خارج از شرکت های غول پیکر، که برای اینترنت گسترده تر انجام شده است، به سادگی وجود ندارد. همچنین اطلاعات کافی از بازیگران اصلی در مورد چند درصد از مشکلات مشتریان آنها مربوط به شبکه وجود ندارد. ما به خوبی از قطعی شبکه ارائه دهندگان بزرگ ابری آگاه هستیم که می توانند یک بخش کامل از اینترنت را برای چند ساعت از بین ببرند، صرفاً به این دلیل که آنها رویدادهای با مشخصات بالایی هستند که تعداد زیادی از افراد و شرکت ها را تحت تأثیر قرار می دهند. قطع شدن شبکه می تواند در موارد بسیار بیشتری مشکلاتی ایجاد کند، حتی اگر همه این موارد در کانون توجه نباشند. مشتریان سرویس های ابری نیز چیزی در مورد علل مشکلات نمی دانند. اگر خرابی وجود داشته باشد، تقریبا غیرممکن است که آن را به خطای شبکه در سمت ارائه دهنده خدمات نسبت دهیم. برای آنها خدمات شخص ثالث جعبه سیاه هستند. ارزیابی تأثیر بدون اینکه یک ارائه دهنده خدمات بزرگ باشد غیرممکن است.

با توجه به آنچه که بازیکنان بزرگ در مورد سیستم های خود گزارش می دهند، می توان گفت که اگر مشکلات شبکه تنها درصد کمی از مشکلات احتمالی خرابی را به خود اختصاص دهد، خوش شانس هستید. ارتباطات شبکه هنوز از چیزهای پیش پا افتاده ای مانند خرابی سخت افزار، تغییرات توپولوژی، تغییرات پیکربندی اداری و قطع برق رنج می برند. اخیراً با تعجب متوجه شدم که لیست مشکلات احتمالی اضافه شده است نیش کوسه (بله درست شنیدی).

ACID به معنای چیزهای مختلفی است

مخفف ACID مخفف عبارت Atomicity, Consistency, Isolation, Reliability است. این ویژگی های تراکنش ها برای اطمینان از اعتبار آنها در صورت خرابی، خطا، خرابی سخت افزاری و غیره در نظر گرفته شده است. بدون ACID یا طرح های مشابه، برای توسعه دهندگان برنامه دشوار خواهد بود که بین آنچه مسئول هستند و آنچه پایگاه داده مسئول آن است، تفاوت قائل شوند. اکثر پایگاه‌های داده تراکنشی رابطه‌ای سعی می‌کنند با ACID سازگار باشند، اما رویکردهای جدید مانند NoSQL باعث ایجاد پایگاه‌های اطلاعاتی بدون تراکنش‌های ACID شده است، زیرا پیاده‌سازی آن‌ها گران است.

زمانی که من برای اولین بار وارد صنعت شدم، رهبر فنی ما در مورد ارتباط مفهوم ACID صحبت کرد. اگر بخواهیم منصفانه باشیم، ACID به‌جای یک استاندارد اجرای دقیق، یک توصیف تقریبی در نظر گرفته می‌شود. امروز آن را بیشتر مفید می دانم زیرا دسته خاصی از مسائل را مطرح می کند (و طیفی از راه حل های ممکن را پیشنهاد می کند).

هر DBMS سازگار با ACID نیست. در عین حال، پیاده‌سازی‌های پایگاه‌داده‌ای که از ACID پشتیبانی می‌کنند، مجموعه الزامات را به طور متفاوتی درک می‌کنند. یکی از دلایلی که پیاده سازی های ACID تکه تکه هستند، به دلیل مبادلات بسیاری است که باید برای اجرای الزامات ACID انجام شود. سازندگان ممکن است پایگاه داده خود را به عنوان سازگار با ACID ارائه کنند، اما تفسیر موارد لبه ممکن است به طور چشمگیری متفاوت باشد، همانطور که مکانیسم مدیریت رویدادهای "بعید" نیز متفاوت است. حداقل، توسعه‌دهندگان می‌توانند درک سطح بالایی از پیچیدگی‌های پیاده‌سازی پایه به دست آورند تا درک مناسبی از رفتار خاص خود و مبادلات طراحی به دست آورند.

بحث در مورد اینکه آیا MongoDB با الزامات ACID مطابقت دارد یا خیر، حتی پس از انتشار نسخه 4 نیز ادامه دارد. MongoDB برای مدت طولانی پشتیبانی نمی شود چوب بری، اگرچه به طور پیش فرض داده ها بیش از یک بار در هر 60 ثانیه به دیسک متعهد نمی شدند. سناریوی زیر را تصور کنید: یک برنامه دو نوشته (w1 و w2) ارسال می کند. MongoDB با موفقیت w1 را ذخیره می کند، اما w2 به دلیل نقص سخت افزار از بین می رود.

توسعه دهندگان بیشتری باید این را در مورد پایگاه داده بدانند
نموداری که سناریو را نشان می دهد. MongoDB قبل از اینکه بتواند داده ها را روی دیسک بنویسد از کار می افتد

تعهد به دیسک فرآیند گرانی است. با اجتناب از تعهدات مکرر، توسعه دهندگان عملکرد ضبط را به بهای قابلیت اطمینان بهبود می بخشند. MongoDB در حال حاضر از ورود به سیستم پشتیبانی می‌کند، اما نوشتن‌های کثیف همچنان می‌توانند بر یکپارچگی داده‌ها تأثیر بگذارند، زیرا گزارش‌ها به طور پیش‌فرض هر ۱۰۰ میلی‌ثانیه ضبط می‌شوند. یعنی هنوز سناریوی مشابهی برای لاگ ها و تغییرات ارائه شده در آنها امکان پذیر است، هرچند که ریسک آن بسیار کمتر است.

هر پایگاه داده سازوکارهای سازگاری و جداسازی خاص خود را دارد

از بین الزامات ACID، سازگاری و انزوا دارای بیشترین تعداد پیاده سازی های مختلف است، زیرا دامنه مبادلات گسترده تر است. باید گفت که سازگاری و انزوا عملکردهای بسیار گران قیمتی هستند. آنها نیاز به هماهنگی و افزایش رقابت برای سازگاری داده ها دارند. پیچیدگی مشکل به طور قابل توجهی افزایش می یابد زمانی که لازم است پایگاه داده به صورت افقی در چندین مرکز داده (به خصوص اگر آنها در مناطق جغرافیایی مختلف واقع شده باشند) مقیاس شوند. دستیابی به سطح بالایی از سازگاری بسیار دشوار است، زیرا همچنین دسترسی را کاهش می دهد و تقسیم بندی شبکه را افزایش می دهد. برای توضیح کلی تر این پدیده توصیه می کنم به ادامه مطلب مراجعه کنید قضیه CAP. همچنین شایان ذکر است که برنامه‌ها می‌توانند مقادیر کمی از ناهماهنگی‌ها را مدیریت کنند و برنامه‌نویسان می‌توانند تفاوت‌های ظریف مشکل را به‌خوبی درک کنند تا منطق اضافی را در برنامه کاربردی برای مدیریت ناسازگاری بدون تکیه بر پایگاه داده برای رسیدگی به آن، پیاده‌سازی کنند.

DBMS ها اغلب سطوح مختلفی از انزوا را ارائه می دهند. توسعه دهندگان برنامه می توانند بر اساس ترجیحات خود مؤثرترین را انتخاب کنند. ایزوله کم امکان افزایش سرعت را فراهم می کند، اما خطر مسابقه داده را نیز افزایش می دهد. عایق بودن بالا این احتمال را کاهش می دهد، اما کار را کند می کند و می تواند منجر به رقابت شود که منجر به ترمزهایی در پایه می شود که خرابی ها شروع می شود.

توسعه دهندگان بیشتری باید این را در مورد پایگاه داده بدانند
بررسی مدل های همزمانی موجود و روابط بین آنها

استاندارد SQL تنها چهار سطح جداسازی را تعریف می کند، اگرچه در تئوری و عملی تعداد بیشتری وجود دارد. Jepson.io یک نمای کلی عالی از مدل های همزمانی موجود ارائه می دهد. برای مثال، Google Spanner سریال‌سازی خارجی را با همگام‌سازی ساعت تضمین می‌کند، و اگرچه این یک لایه جداسازی سخت‌گیرانه‌تر است، اما در لایه‌های ایزوله استاندارد تعریف نشده است.

استاندارد SQL سطوح جداسازی زیر را ذکر می کند:

  • Serializable (سخت‌ترین و گران‌ترین): اجرای سریال‌سازی‌شده همان تأثیر برخی از اجرای تراکنش‌های متوالی را دارد. اجرای متوالی به این معنی است که هر تراکنش بعدی تنها پس از تکمیل تراکنش قبلی آغاز می شود. لازم به ذکر است که سطح Serializable اغلب به‌عنوان جداسازی عکس فوری (مثلاً در اوراکل) به دلیل تفاوت در تفسیر اجرا می‌شود، اگرچه خود جداسازی عکس فوری در استاندارد SQL نشان داده نمی‌شود.
  • خواندن های تکراری: سوابق غیرمتعهد در تراکنش فعلی برای تراکنش فعلی موجود است، اما تغییراتی که توسط سایر تراکنش‌ها ایجاد شده است (مانند ردیف‌های جدید) غیر قابل دیدن.
  • متعهد بخوانید: داده های غیرمتعهد برای تراکنش ها در دسترس نیست. در این حالت، تراکنش‌ها فقط می‌توانند داده‌های متعهد را ببینند و ممکن است خواندن فانتوم رخ دهد. اگر تراکنش ردیف‌های جدیدی را درج و متعهد کند، تراکنش فعلی می‌تواند آن‌ها را هنگام پرس و جو ببیند.
  • بدون تعهد بخوانید (کمترین سطح سخت و گران قیمت): خواندن کثیف مجاز است، تراکنش ها می توانند تغییرات غیرمتعهد ایجاد شده توسط سایر تراکنش ها را ببینند. در عمل، این سطح ممکن است برای برآوردهای تقریبی، مانند پرس و جو مفید باشد COUNT(*) روی میز.

سطح Serializable خطر مسابقه داده ها را به حداقل می رساند، در حالی که گران ترین برای پیاده سازی است و منجر به بالاترین بار رقابتی در سیستم می شود. اجرای سایر سطوح جداسازی آسان تر است، اما احتمال مسابقه داده ها را افزایش می دهد. برخی از DBMS ها به شما امکان می دهند یک سطح جداسازی سفارشی تنظیم کنید، برخی دیگر ترجیحات قوی دارند و همه سطوح پشتیبانی نمی شوند.

پشتیبانی از سطوح جداسازی اغلب در یک DBMS معین تبلیغ می‌شود، اما تنها مطالعه دقیق رفتار آن می‌تواند نشان دهد که واقعاً چه اتفاقی می‌افتد.

توسعه دهندگان بیشتری باید این را در مورد پایگاه داده بدانند
بررسی ناهنجاری های همزمانی در سطوح مختلف جداسازی برای DBMS های مختلف

مارتین کلپمن در پروژه خود زاهد سطوح مختلف جداسازی را مقایسه می‌کند، درباره ناهنجاری‌های همزمانی صحبت می‌کند، و اینکه آیا پایگاه داده می‌تواند به سطح جداسازی خاصی پایبند باشد یا خیر. تحقیقات کلپمن نشان می دهد که توسعه دهندگان پایگاه داده در مورد سطوح جداسازی چقدر متفاوت فکر می کنند.

انسداد خوش بینانه زمانی به کمک می آید که حفظ حالت معمول دشوار باشد.

مسدود کردن می تواند بسیار گران باشد، نه تنها به این دلیل که رقابت را در پایگاه داده افزایش می دهد، بلکه به این دلیل که سرورهای برنامه را به طور مداوم به پایگاه داده متصل می کند. تقسیم بندی شبکه می تواند موقعیت های قفل انحصاری را تشدید کند و منجر به بن بست هایی شود که شناسایی و حل آنها دشوار است. در مواردی که قفل انحصاری مناسب نیست، قفل خوش بینانه کمک می کند.

قفل خوش بینانه روشی است که در آن هنگام خواندن یک رشته، نسخه، جمع کنترل یا زمان آخرین اصلاح آن را در نظر می گیرد. این به شما امکان می دهد قبل از تغییر یک ورودی اطمینان حاصل کنید که هیچ تغییری در نسخه اتمی وجود ندارد:

UPDATE products
SET name = 'Telegraph receiver', version = 2
WHERE id = 1 AND version = 1

در این مورد، به روز رسانی جدول products اگر عملیات دیگری قبلاً در این ردیف تغییراتی ایجاد کرده باشد، انجام نخواهد شد. اگر هیچ عملیات دیگری روی این ردیف انجام نشده باشد، تغییر برای یک ردیف رخ می دهد و می توان گفت که آپدیت با موفقیت انجام شده است.

غیر از خواندن کثیف و از دست دادن اطلاعات، ناهنجاری های دیگری نیز وجود دارد

وقتی نوبت به سازگاری داده‌ها می‌رسد، تمرکز روی پتانسیل شرایط مسابقه است که می‌تواند منجر به خواندن کثیف و از دست رفتن داده شود. با این حال، ناهنجاری های داده ها به همین جا ختم نمی شود.

یکی از نمونه های این ناهنجاری ها، اعوجاج ثبت است (کج ها بنویس). تشخیص تحریف ها دشوار است، زیرا معمولاً به طور فعال به دنبال آنها نیستند. آنها به دلیل خواندن کثیف یا از دست دادن داده ها نیستند، بلکه به دلیل نقض محدودیت های منطقی اعمال شده روی داده ها هستند.

به عنوان مثال، اجازه دهید یک برنامه نظارتی را در نظر بگیریم که به یک اپراتور نیاز دارد که همیشه در حال تماس باشد:

BEGIN tx1;                      BEGIN tx2;
SELECT COUNT(*)
FROM operators
WHERE oncall = true;
0                               SELECT COUNT(*)
                                FROM operators
                                WHERE oncall = TRUE;
                                0
UPDATE operators                UPDATE operators
SET oncall = TRUE               SET oncall = TRUE
WHERE userId = 4;               WHERE userId = 2;
COMMIT tx1;                     COMMIT tx2;

در شرایط فوق، در صورتی که هر دو تراکنش با موفقیت انجام شوند، یک رکورد فساد رخ خواهد داد. اگرچه هیچ خواندن کثیفی یا از دست دادن داده وجود نداشت، یکپارچگی داده ها به خطر افتاد: اکنون دو نفر به طور همزمان در حال تماس هستند.

جداسازی قابل سریال‌سازی، طراحی طرحواره یا محدودیت‌های پایگاه داده می‌تواند به حذف خرابی نوشتن کمک کند. توسعه دهندگان باید بتوانند چنین ناهنجاری هایی را در طول توسعه شناسایی کنند تا از آنها در تولید جلوگیری شود. در عین حال، جستجوی اعوجاج های ضبط در پایه کد بسیار دشوار است. به خصوص در سیستم های بزرگ، زمانی که تیم های توسعه مختلف بر اساس جداول یکسان، مسئولیت اجرای توابع را بر عهده دارند و در مورد مشخصات دسترسی به داده ها توافق ندارند.

پایگاه داده و کاربر همیشه در مورد اینکه چه کاری انجام دهند توافق ندارند

یکی از ویژگی های کلیدی پایگاه های داده ضمانت اجرای دستور است، اما ممکن است خود این دستور برای توسعه دهنده نرم افزار شفاف نباشد. پایگاه های داده تراکنش ها را به ترتیبی که دریافت می کنند انجام می دهند، نه به ترتیبی که برنامه نویسان در نظر دارند. پیش‌بینی ترتیب تراکنش‌ها به‌ویژه در سیستم‌های موازی با بارگذاری بالا دشوار است.

در طول توسعه، به‌ویژه هنگام کار با کتابخانه‌های غیرمسدود، سبک ضعیف و خوانایی پایین می‌تواند باعث شود کاربران این باور را داشته باشند که تراکنش‌ها به صورت متوالی اجرا می‌شوند، در حالی که در واقع می‌توانند به هر ترتیبی به پایگاه داده وارد شوند.

در نگاه اول، در برنامه زیر، T1 و T2 به ترتیب فراخوانی می شوند، اما اگر این توابع غیر مسدود هستند و بلافاصله نتیجه را به شکل برمی گرداند. وعده، سپس ترتیب تماس ها با لحظات ورود آنها به پایگاه داده تعیین می شود:

result1 = T1() // نتایج واقعی وعده هستند
result2 = T2()

اگر اتمیسیته مورد نیاز است (یعنی یا تمام عملیات باید تکمیل شود یا متوقف شود) و توالی مهم باشد، عملیات T1 و T2 باید در یک تراکنش انجام شود.

اشتراک گذاری در سطح برنامه را می توان به خارج از برنامه منتقل کرد

Sharding روشی برای پارتیشن بندی افقی پایگاه داده است. برخی از پایگاه های داده می توانند به طور خودکار داده ها را به صورت افقی تقسیم کنند، در حالی که برخی دیگر نمی توانند یا در آن خیلی خوب نیستند. هنگامی که معماران/توسعه دهندگان داده قادر به پیش بینی دقیق نحوه دسترسی به داده ها باشند، می توانند به جای واگذاری این کار به پایگاه داده، پارتیشن های افقی را در فضای کاربر ایجاد کنند. به این فرآیند "شاربندی در سطح برنامه" می گویند. (اشتراک گذاری در سطح برنامه).

متأسفانه، این نام اغلب این تصور غلط را ایجاد می کند که اشتراک گذاری در سرویس های برنامه زندگی می کند. در واقع می توان آن را به صورت یک لایه مجزا در مقابل پایگاه داده پیاده سازی کرد. بسته به رشد داده ها و تکرار طرحواره، الزامات اشتراک گذاری می تواند بسیار پیچیده شود. برخی از استراتژی ها ممکن است از توانایی تکرار بدون نیاز به استقرار مجدد سرورهای برنامه بهره مند شوند.

توسعه دهندگان بیشتری باید این را در مورد پایگاه داده بدانند
نمونه ای از معماری که در آن سرورهای برنامه از سرویس اشتراک گذاری جدا می شوند

انتقال اشتراک‌گذاری به یک سرویس جداگانه، امکان استفاده از استراتژی‌های اشتراک‌گذاری مختلف را بدون نیاز به استفاده مجدد از برنامه‌ها افزایش می‌دهد. Vitess نمونه ای از چنین سیستم اشتراک گذاری در سطح برنامه است. Vitess اشتراک گذاری افقی را برای MySQL فراهم می کند و به مشتریان اجازه می دهد تا از طریق پروتکل MySQL به آن متصل شوند. سیستم داده ها را به گره های MySQL مختلف تقسیم می کند که هیچ چیز در مورد یکدیگر نمی دانند.

افزایش خودکار می تواند خطرناک باشد

AUTOINCREMENT یک روش رایج برای تولید کلیدهای اولیه است. اغلب مواردی وجود دارد که از پایگاه های داده به عنوان تولید کننده شناسه استفاده می شود و پایگاه داده حاوی جداولی است که برای تولید شناسه ها طراحی شده اند. دلایل متعددی وجود دارد که چرا تولید کلیدهای اولیه با استفاده از افزایش خودکار بسیار ایده آل نیست:

  • در یک پایگاه داده توزیع شده، افزایش خودکار یک مشکل جدی است. برای تولید شناسه، یک قفل جهانی لازم است. در عوض، می توانید یک UUID ایجاد کنید: این نیازی به تعامل بین گره های مختلف پایگاه داده ندارد. افزایش خودکار با قفل ها می تواند منجر به مشاجره شود و به طور قابل توجهی عملکرد درج ها را در موقعیت های توزیع شده کاهش دهد. برخی از DBMS ها (مثلاً MySQL) ممکن است به پیکربندی خاص و توجه دقیق تری برای سازماندهی صحیح نسخه Master-Master نیاز داشته باشند. و اشتباه کردن هنگام پیکربندی آسان است که منجر به شکست در ضبط می شود.
  • برخی از پایگاه های داده دارای الگوریتم های پارتیشن بندی بر اساس کلیدهای اولیه هستند. شناسه‌های متوالی می‌توانند به نقاط داغ غیرقابل پیش‌بینی و افزایش بار روی برخی پارتیشن‌ها منجر شوند، در حالی که برخی دیگر بی‌کار می‌مانند.
  • کلید اصلی سریعترین راه برای دسترسی به یک ردیف در پایگاه داده است. با روش‌های بهتر برای شناسایی رکوردها، شناسه‌های متوالی می‌توانند مهم‌ترین ستون در جداول را به ستونی بی‌فایده پر از مقادیر بی‌معنا تبدیل کنند. بنابراین، در صورت امکان، لطفاً یک کلید اولیه منحصر به فرد و طبیعی در سطح جهانی (به عنوان مثال نام کاربری) انتخاب کنید.

قبل از تصمیم گیری در مورد یک رویکرد، تأثیر افزایش خودکار شناسه ها و UUID ها بر نمایه سازی، پارتیشن بندی و اشتراک گذاری را در نظر بگیرید.

داده های قدیمی می توانند مفید باشند و نیازی به قفل شدن نداشته باشند

Multiversion Concurrency Control (MVCC) بسیاری از الزامات سازگاری را که در بالا به اختصار مورد بحث قرار گرفت را اجرا می کند. برخی از پایگاه های داده (به عنوان مثال، Postgres، Spanner) از MVCC برای "تغذیه" تراکنش ها با عکس های فوری استفاده می کنند - نسخه های قدیمی پایگاه داده. تراکنش های اسنپ شات نیز می توانند برای اطمینان از ثبات، سریال شوند. هنگام خواندن از یک عکس فوری قدیمی، داده های قدیمی خوانده می شوند.

خواندن داده های کمی کهنه می تواند مفید باشد، برای مثال، هنگام تولید تجزیه و تحلیل از داده ها یا محاسبه مقادیر تقریبی کل.

اولین مزیت کار با داده های قدیمی تاخیر کم است (به خصوص اگر پایگاه داده در مناطق جغرافیایی مختلف توزیع شده باشد). مورد دوم این است که تراکنش های فقط خواندنی بدون قفل هستند. این یک مزیت قابل توجه برای برنامه هایی است که زیاد مطالعه می کنند، تا زمانی که بتوانند داده های قدیمی را مدیریت کنند.

توسعه دهندگان بیشتری باید این را در مورد پایگاه داده بدانند
سرور برنامه داده ها را از ماکت محلی می خواند که 5 ثانیه قدیمی است، حتی اگر آخرین نسخه در آن سوی اقیانوس آرام موجود باشد.

DBMS ها به طور خودکار نسخه های قدیمی را پاک می کنند و در برخی موارد به شما اجازه می دهند در صورت درخواست این کار را انجام دهید. به عنوان مثال، Postgres به کاربران این امکان را می دهد VACUUM در صورت درخواست، و همچنین به صورت دوره ای این عملیات را به صورت خودکار انجام می دهد. Spanner برای خلاص شدن از شر عکس های قدیمی تر از یک ساعت، یک زباله جمع کن راه اندازی می کند.

هر منبع زمانی در معرض تحریف است

بهترین راز در علم کامپیوتر این است که همه APIهای زمان‌بندی دروغ می‌گویند. در واقع ماشین های ما زمان دقیق فعلی را نمی دانند. رایانه‌ها حاوی کریستال‌های کوارتز هستند که ارتعاشاتی تولید می‌کنند که برای حفظ زمان استفاده می‌شوند. با این حال، آنها به اندازه کافی دقیق نیستند و ممکن است از زمان دقیق جلوتر یا عقب باشند. شیفت می تواند به 20 ثانیه در روز برسد. بنابراین، زمان در رایانه های ما باید به طور دوره ای با زمان شبکه همگام شود.

سرورهای NTP برای همگام‌سازی استفاده می‌شوند، اما فرآیند همگام‌سازی خود در معرض تاخیرهای شبکه است. حتی همگام سازی با یک سرور NTP در همان مرکز داده مدتی طول می کشد. واضح است که کار با یک سرور NTP عمومی می تواند منجر به اعوجاج بیشتر شود.

ساعت‌های اتمی و همتایان GPS آن‌ها برای تعیین زمان فعلی بهتر هستند، اما گران هستند و نیاز به تنظیمات پیچیده‌ای دارند، بنابراین نمی‌توان آن‌ها را روی هر خودرویی نصب کرد. به همین دلیل، مراکز داده از یک رویکرد لایه ای استفاده می کنند. ساعت‌های اتمی و/یا GPS زمان دقیق را نشان می‌دهند و پس از آن از طریق سرورهای ثانویه به ماشین‌های دیگر پخش می‌شود. این به این معنی است که هر دستگاه نسبت به زمان دقیق، جبران معینی را تجربه خواهد کرد.

این وضعیت با این واقعیت تشدید می شود که برنامه ها و پایگاه های داده اغلب در ماشین های مختلف (اگر نه در مراکز داده مختلف) قرار دارند. بنابراین، زمان نه تنها در گره‌های DB توزیع شده در ماشین‌های مختلف متفاوت خواهد بود. همچنین در سرور برنامه متفاوت خواهد بود.

Google TrueTime رویکردی کاملاً متفاوت دارد. اکثر مردم بر این باورند که پیشرفت گوگل در این مسیر با انتقال پیش پا افتاده به ساعت های اتمی و GPS توضیح داده می شود، اما این تنها بخشی از تصویر بزرگ است. در اینجا نحوه کار TrueTime آمده است:

  • TrueTime از دو منبع مختلف استفاده می کند: GPS و ساعت اتمی. این ساعت ها حالت های خرابی غیر همبسته دارند. [برای جزئیات بیشتر به صفحه 5 مراجعه کنید اینجا - تقریبا ترجمه.)بنابراین استفاده مشترک از آنها قابلیت اطمینان را افزایش می دهد.
  • TrueTime یک API غیر معمول دارد. زمان را به صورت بازه ای با خطای اندازه گیری و عدم قطعیت درون آن برمی گرداند. لحظه واقعی در زمان جایی بین مرزهای بالایی و پایینی فاصله است. Spanner، پایگاه داده توزیع شده Google، به سادگی منتظر می ماند تا بتوان گفت که زمان فعلی خارج از محدوده است. این روش مقداری تأخیر را به سیستم وارد می‌کند، به خصوص اگر عدم قطعیت در Masters زیاد باشد، اما صحت را حتی در یک موقعیت توزیع جهانی تضمین می‌کند.

توسعه دهندگان بیشتری باید این را در مورد پایگاه داده بدانند
مؤلفه‌های Spanner از TrueTime استفاده می‌کنند، جایی که TT.now() بازه‌ای را برمی‌گرداند، بنابراین Spanner به سادگی می‌خوابد تا زمانی که بتواند مطمئن شود که زمان فعلی از یک نقطه خاص گذشته است.

کاهش دقت در تعیین زمان جاری به معنای افزایش مدت زمان عملیات Spanner و کاهش عملکرد است. به همین دلیل است که حفظ بالاترین دقت ممکن حتی با وجود غیرممکن بودن دستیابی به یک ساعت کاملا دقیق بسیار مهم است.

تاخیر معانی زیادی دارد

اگر از ده ها متخصص در مورد اینکه تاخیر چیست بپرسید، احتمالاً پاسخ های متفاوتی دریافت خواهید کرد. در DBMS تأخیر اغلب «تأخیر پایگاه داده» نامیده می‌شود و با آنچه مشتری درک می‌کند متفاوت است. واقعیت این است که مشتری مجموع تاخیر شبکه و تاخیر پایگاه داده را مشاهده می کند. توانایی جداسازی نوع تأخیر هنگام رفع اشکال مشکلات در حال رشد بسیار مهم است. هنگام جمع آوری و نمایش معیارها، همیشه سعی کنید به هر دو نوع توجه داشته باشید.

الزامات عملکرد باید برای یک معامله خاص ارزیابی شود

گاهی اوقات ویژگی‌های عملکرد یک DBMS و محدودیت‌های آن از نظر توان نوشتن/خواندن و تأخیر مشخص می‌شوند. این یک نمای کلی از پارامترهای کلیدی سیستم را ارائه می دهد، اما هنگام ارزیابی عملکرد یک DBMS جدید، یک رویکرد بسیار جامع تر، ارزیابی جداگانه عملیات حیاتی (برای هر پرس و جو و/یا تراکنش) است. مثال ها:

  • هنگام درج یک سطر جدید در جدول X (با 50 میلیون سطر) با محدودیت های مشخص شده و padding ردیف در جداول مرتبط، خروجی و تأخیر را بنویسید.
  • تاخیر در نمایش دوستان دوستان یک کاربر خاص زمانی که میانگین تعداد دوستان 500 نفر باشد.
  • تأخیر در بازیابی 100 ورودی برتر از تاریخچه کاربر زمانی که کاربر 500 کاربر دیگر را با X ورودی در ساعت دنبال می کند.

ارزیابی و آزمایش ممکن است شامل چنین موارد مهمی باشد تا زمانی که مطمئن شوید که پایگاه داده الزامات عملکرد را برآورده می کند. یک قانون سرانگشتی مشابه نیز این تفکیک را هنگام جمع‌آوری معیارهای تأخیر و تعیین SLOها در نظر می‌گیرد.

هنگام جمع آوری معیارها برای هر عملیات، از کاردینالیته بالا آگاه باشید. از گزارش‌ها، جمع‌آوری رویداد، یا ردیابی توزیع‌شده برای به دست آوردن داده‌های اشکال‌زدایی با قدرت بالا استفاده کنید. در مقاله "آیا می خواهید تأخیر را اشکال زدایی کنید؟» می‌توانید با روش‌های تأخیر اشکال‌زدایی آشنا شوید.

تراکنش های تو در تو می توانند خطرناک باشند

هر DBMS از تراکنش‌های تودرتو پشتیبانی نمی‌کند، اما زمانی که انجام می‌دهند، چنین تراکنش‌هایی می‌توانند منجر به خطاهای غیرمنتظره‌ای شوند که شناسایی آن‌ها همیشه آسان نیست (یعنی باید واضح باشد که نوعی ناهنجاری وجود دارد).

می‌توانید از استفاده از تراکنش‌های تودرتو با استفاده از کتابخانه‌های مشتری که می‌توانند آنها را شناسایی و دور بزنند، اجتناب کنید. اگر تراکنش‌های تودرتو را نمی‌توان رها کرد، در اجرای آنها دقت ویژه‌ای داشته باشید تا از موقعیت‌های غیرمنتظره‌ای که تراکنش‌های تکمیل‌شده به‌دلیل تراکنش‌های تودرتو به‌طور تصادفی لغو می‌شوند، جلوگیری کنید.

محصور کردن تراکنش ها در لایه های مختلف می تواند منجر به تراکنش های تودرتوی غیرمنتظره شود و از نقطه نظر خوانایی کد، درک مقاصد نویسنده را دشوار می کند. به برنامه زیر توجه کنید:

with newTransaction():
   Accounts.create("609-543-222")
   with newTransaction():
       Accounts.create("775-988-322")
       throw Rollback();

خروجی کد بالا چه خواهد بود؟ آیا هر دو تراکنش را برمی گرداند یا فقط تراکنش داخلی را؟ چه اتفاقی می‌افتد اگر به لایه‌های متعددی از کتابخانه‌ها تکیه کنیم که ایجاد تراکنش‌ها را برای ما محصور می‌کنند؟ آیا ما قادر به شناسایی و بهبود چنین مواردی خواهیم بود؟

یک لایه داده با چندین عملیات (مثلاً newAccount) قبلاً در تراکنش های خود پیاده سازی شده است. چه اتفاقی می افتد اگر آنها را به عنوان بخشی از منطق تجاری سطح بالاتری که در تراکنش خود اجرا می شود اجرا کنید؟ انزوا و سازگاری در این مورد چه خواهد بود؟

function newAccount(id string) {
  with newTransaction():
      Accounts.create(id)
}

به جای جستجوی پاسخ برای چنین سوالات بی پایانی، بهتر است از تراکنش های تودرتو خودداری کنید. پس از همه، لایه داده شما می تواند به راحتی عملیات سطح بالا را بدون ایجاد تراکنش های خود انجام دهد. علاوه بر این، منطق تجاری خود قادر به شروع یک معامله، انجام عملیات بر روی آن، ارتکاب یا لغو یک تراکنش است.

function newAccount(id string) {
   Accounts.create(id)
}
// In main application:
with newTransaction():
   // Read some data from database for configuration.
   // Generate an ID from the ID service.
   Accounts.create(id)
   Uploads.create(id) // create upload queue for the user.

تراکنش ها نباید به وضعیت برنامه مرتبط باشند

گاهی اوقات استفاده از وضعیت برنامه در تراکنش ها برای تغییر مقادیر خاص یا تغییر پارامترهای پرس و جو وسوسه انگیز است. نکته مهمی که باید در نظر گرفته شود، دامنه صحیح کاربرد است. وقتی مشکلات شبکه وجود دارد، مشتریان اغلب تراکنش ها را مجدداً راه اندازی می کنند. اگر تراکنش به حالتی بستگی داشته باشد که توسط فرآیند دیگری تغییر می کند، ممکن است بسته به احتمال مسابقه داده، مقدار اشتباهی را انتخاب کند. تراکنش ها باید خطر شرایط مسابقه داده در برنامه را در نظر بگیرند.

var seq int64
with newTransaction():
    newSeq := atomic.Increment(&seq)
    Entries.query(newSeq)
    // Other operations...

تراکنش فوق بدون در نظر گرفتن نتیجه نهایی، هر بار که انجام می شود، شماره دنباله را افزایش می دهد. اگر commit به دلیل مشکلات شبکه با شکست مواجه شود، هنگامی که دوباره امتحان کنید، درخواست با شماره ترتیب دیگری اجرا می شود.

برنامه ریزان پرس و جو می توانند چیزهای زیادی در مورد پایگاه داده به شما بگویند

برنامه ریزان پرس و جو تعیین می کنند که چگونه یک پرس و جو در یک پایگاه داده اجرا شود. آنها همچنین درخواست ها را تجزیه و تحلیل و قبل از ارسال بهینه سازی می کنند. برنامه ریزان فقط می توانند برخی برآوردهای ممکن را بر اساس سیگنال هایی که در اختیار دارند ارائه دهند. به عنوان مثال، بهترین روش جستجو برای پرس و جو زیر چیست؟

SELECT * FROM articles where author = "rakyll" order by title;

نتایج را می توان به دو روش بازیابی کرد:

  • اسکن کامل جدول: می توانید به هر ورودی در جدول نگاه کنید و مقاله ها را با نام نویسنده منطبق برگردانید و سپس آنها را سفارش دهید.
  • اسکن نمایه: می توانید از یک نمایه برای پیدا کردن شناسه های منطبق، دریافت آن ردیف ها و سپس سفارش آنها استفاده کنید.

وظیفه برنامه ریز پرس و جو این است که تعیین کند کدام استراتژی بهترین است. شایان ذکر است که برنامه ریزان پرس و جو فقط قابلیت های پیش بینی محدودی دارند. این می تواند منجر به تصمیمات بد شود. DBA ها یا توسعه دهندگان می توانند از آنها برای تشخیص و تنظیم دقیق پرس و جوهایی که عملکرد ضعیف دارند استفاده کنند. نسخه‌های جدید DBMS می‌توانند برنامه‌ریزان پرس و جو را پیکربندی کنند و اگر نسخه جدید منجر به مشکلات عملکردی شود، تشخیص خود به هنگام به‌روزرسانی پایگاه داده می‌تواند کمک کند. گزارش‌های کند پرس و جو، گزارش‌های مربوط به تأخیر یا آمار زمان اجرا می‌توانند به شناسایی پرسش‌هایی که نیاز به بهینه‌سازی دارند کمک کنند.

برخی از معیارهای ارائه شده توسط برنامه ریز پرس و جو ممکن است در معرض نویز باشند (مخصوصاً هنگام تخمین تأخیر یا زمان CPU). ابزاری برای ردیابی و ردیابی مسیر اجرا، یک افزونه خوب برای برنامه‌ریزها هستند. آنها به شما امکان می دهند چنین مشکلاتی را تشخیص دهید (افسوس که همه DBMS ها چنین ابزارهایی را ارائه نمی دهند).

مهاجرت آنلاین دشوار است اما ممکن است

مهاجرت آنلاین، مهاجرت زنده یا انتقال بلادرنگ به معنای انتقال از یک پایگاه داده به پایگاه داده دیگر بدون خرابی یا خرابی داده ها است. اگر انتقال در همان DBMS/موتور انجام شود، مهاجرت زنده آسان‌تر است. وضعیت زمانی پیچیده تر می شود که لازم باشد به یک DBMS جدید با الزامات عملکرد و طرحواره های مختلف حرکت کنیم.

مدل های مهاجرت آنلاین مختلفی وجود دارد. اینجا یکی از آنها است:

  • دوبار ورود را در هر دو پایگاه داده فعال کنید. پایگاه داده جدید در این مرحله همه داده ها را ندارد، بلکه فقط آخرین داده ها را می پذیرد. پس از اطمینان از این موضوع، می توانید به مرحله بعدی بروید.
  • خواندن از هر دو پایگاه داده را فعال کنید.
  • سیستم را به گونه ای پیکربندی کنید که خواندن و نوشتن عمدتاً در پایگاه داده جدید انجام شود.
  • نوشتن در پایگاه داده قدیمی را متوقف کنید و به خواندن داده ها از آن ادامه دهید. در این مرحله، پایگاه داده جدید هنوز از برخی داده ها خالی است. آنها باید از پایگاه داده قدیمی کپی شوند.
  • پایگاه داده قدیمی فقط خواندنی است. داده های گم شده را از پایگاه داده قدیمی در پایگاه داده جدید کپی کنید. پس از اتمام انتقال، مسیرها را به پایگاه داده جدید تغییر دهید و قدیمی را متوقف کرده و آن را از سیستم حذف کنید.

برای اطلاعات بیشتر، توصیه می کنم تماس بگیرید مقاله، که استراتژی مهاجرت Stripe بر اساس این مدل را شرح می دهد.

افزایش قابل توجه در پایگاه داده مستلزم افزایش غیرقابل پیش بینی بودن است

رشد پایگاه داده منجر به مشکلات غیرقابل پیش بینی مرتبط با مقیاس آن می شود. هرچه بیشتر در مورد ساختار داخلی یک پایگاه داده بدانیم، بهتر می توانیم مقیاس آن را پیش بینی کنیم. با این حال، هنوز نمی توان برخی از لحظات را پیش بینی کرد.
همانطور که پایه رشد می کند، مفروضات و انتظارات قبلی در مورد حجم داده و نیازهای پهنای باند شبکه ممکن است قدیمی شوند. این زمانی است که سوال در مورد تعمیرات اساسی طراحی، بهبودهای عملیاتی در مقیاس بزرگ، بازنگری در استقرار یا مهاجرت به دیگر DBMS ها برای جلوگیری از مشکلات احتمالی مطرح می شود.

اما فکر نکنید که دانش عالی از ساختار داخلی پایگاه داده موجود تنها چیزی است که لازم است. مقیاس های جدید ناشناخته های جدیدی را با خود به همراه خواهند داشت. نقاط درد غیرقابل پیش‌بینی، توزیع نابرابر داده‌ها، پهنای باند و مشکلات سخت‌افزاری غیرمنتظره، ترافیک روزافزون و بخش‌های جدید شبکه شما را مجبور می‌کند تا در رویکرد پایگاه داده، مدل داده، مدل استقرار و اندازه پایگاه داده خود تجدید نظر کنید.

...

در زمانی که به انتشار این مقاله فکر کردم، پنج مورد دیگر در لیست اصلی من وجود داشت. سپس تعداد زیادی آمد ایده جدید در مورد موارد دیگری که می توان پوشش داد. بنابراین، مقاله به کمترین مشکلاتی که نیاز به حداکثر توجه دارند می پردازد. با این حال، این بدان معنا نیست که موضوع تمام شده است و من دیگر در مطالب آینده خود به آن باز نمی گردم و تغییراتی در مورد فعلی ایجاد نمی کنم.

PS

در وبلاگ ما نیز بخوانید:

منبع: www.habr.com

اضافه کردن نظر