محمدحسین کریمی
محمدحسین کریمی
خواندن ۴ دقیقه·۳ ماه پیش

نجات MySQL از Corruption

MySQL Logo
MySQL Logo

خوب توی این مقاله به این اشاره شد که چه اتفاقی برای دیتابیس افتاد حالا اول دقیقتر بریم داخل سیستم ببینم چه بلایی سر دیتابیس اومده

موقع استارت شدن پروسس mysqld با خطاهای از قبیل خطای زیر مواجه میشدیم که حاکی از این بود که دیتابیس خود MySQL که دیتابیس مهمی هستش دچار مشکل شده (مثلا اینجا به جدولی که داره اطلاعات جداول رو نگهداری میکنه اشاره شده)

[ERROR] [MY-012855] [InnoDB] Clustered record for sec rec not found index schema_id of table mysql.tables
InnoDB: sec index record PHYSICAL RECORD: n_fields 3; compact format; info bits 0

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

و خوب به تغییر کانفیگ یا force recovery توی لاگ استک تریس خود MySQL رو میگرفتیم که احتمال اتفاقاتی مثل مموری لیک رو بیشتر میکرد

معمولا وقتی رخ میده که تو محیط های کلاود هستیم و دیتابیس از limit کانتینرش خبر نداره و خوب از کل مموری استفاده میکنه و برای جلوگیری ازش فقط باید دیتابیس رو بهینه کانفیگ کرد (مخصوصا موتور ذخیره سازی رو)

تقریبا از استارت شدن دیتابیس نا امید شده بودیم چون دیتابیس اصلی mysql خراب شده بود و force recovery هم جواب نمیداد.

بعد از گشتن توی ابزار های MySQL به یه ابزاری به اسم mysqlfrm پیدا کردیم که کارش این بوده که توی قبل از نسخه ۸ MySQL فایل های frm رو (که برای موتور ذخیره سازی MyISAM هستش) رو بخونه و دیتا رو بتونیم وارد کنیم. خوب دنبال یه همچین ابزاری برای نسخه ۸ بودیم (چون MyISAM دیگه استفاده نمیشد و جاشو به InooDB داده بود) که خوشبختانه پیدا شد. به اسم idb2sdi که این کار رو برای موتور InnoDB انجام میداد.

خوب حالا کار این ابزار چیه؟

قبلش باید ببینیم که توی هر فایل idb چی ذخیره میشه؟ هر موتور ذخیره سازی توی MySQL به یه روشی داده ها و ساختار جداول رو نگهداری میکنه. موتور InnoDB از طریق sdi ساختار جداول رو و از طریق idb داده ها رو نگهداری میکنه ولی خوب به صورت پیشفرض برای هر جدول از ۱ فایل برای انجام این کار استفاده میکنه (اینکه به ازایه هر جدول فایل جدا استفاده یا نه هم قابل تنظیمه) یعنی توی فایل idb هم ساختار جدول (که میشه محتوای sdi) و هم داده ها رو ذخیره میکنه

این ابزار کارش جدا کردن اینا از همه یعنی میتونیم به ساختار دیتابیس برسیم (البته خروجیش JSONعه) ولی میتونیم فایل روی Parse کنیم و جداول رو بازسازی کنیم)

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

به هر فایل idb ما میگیم tablespace

اول با این دستور tablespace پیشفرض مربوط به جدول رو از بین میبریم و به موتور دیتابیس میگیم که باهاش کاری نداشته باشه

ALTER TABLE `{tbl_name}` DISCARD TABLESPACE;

و بعد از اون فایل idb رو توی یه همچین ادرسی کپی میکنیم:

/var/lib/mysql/{databasename}/{tbl_name}.idb

و با دستور زیر به موتور ذخیره سازی داده هامون میگیم که tablespace وارد شده رو لود کنه و ازش استفاده کنه

ALTER TABLE `{tbl_name}` IMPORT TABLESPACE

و بعد از گذروندن این مراحل به ازایه هر جدول داده ها رو داریم

نکته: اگه تو سرور قبلی ایندکس خراب داشته باشید اون ایندکس اینجا هم خرابه پس میتونیم اونا رو قبل شروع DROP کنیم

این همه مرحله رو دستی انجام بدیم؟

قطعا نه. مخصوصا وقتی که کلی جدول داریم و فرمت JSON مربوط به sdi باید کامل باهاش اشنا باشیم اونجا پر ENUM های داخلی مربوط به MySQL هستش و کلی چیز دیگه.

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

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

البته روز بعد تغییراتمون (که شامل پشتیبانی از یه سری فیلد خاص و داکیومنت بهتر و..) و همچنین پشتیبانی Docker و Kubernetes رو به این ابزار اضافه کردیم و تحت PR های متفاوت به کد اصلی اضافه کردیم.

چیکار کنیم که اینجوری نشیم؟

توی محیط هایی مثل K8s و Docker که محدودیت منابع تنظیم میکنید برای دیتابیس باید دیتابیس رو هم جوری کانفیگ بکنید که بیشتر از اون عدد مصرف نکنه (میتونید از Config Generator ها کمک بگیرید احتمالا) چون وقتی اطلاعات سیستم رو دیتابیس دریافت میکنه منابع هاست رو میبینه و خوب ممکنه دچار OOMKill بشید.

حواستون حتما به GraceFullShutdown باشه (اگه حجم دیتابیس بالاست تایم اوتش رو هم زیاد کنیم که force kill نشه)

گیت هابmysqlدیتابیسkubernetesdocker
مهندس دوآپس و برنامه نویس علاقه مند به دنیای OpenSource
شاید از این پست‌ها خوشتان بیاید