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

حداکثر کارایی در MySQL InnoDB Cluster

MySQL InnoDB Cluster
MySQL InnoDB Cluster


ما برای ایجاد یک کلاستر ۳ عضوی از MySQL نسخه ۸ استفاده میکنیم. برای شروع، سرور باید تنظیمات زیر را هنگام bootstrap شدن اعمال کند.

enforce_gtid_consistency=ON

یکم در مورد GTID توضیح بدم. GTID مخفف Global Transaction Identfication هست و وقتی شما چند سرور MySQL رو Cluster کردید، اگر در سرور Primary تراکنشی انجام بشه بهش یک کد اختصاص داده میشه که شامل server_uuid و یک عدد هست که به صورت ترتیبی از ۱ تا ۴/۲۹۴/۹۶۷/۲۹۵ ادامه پیدا میکنه و با : از هم جدا شدند. مثلا 3E11FA47-71CA-11E1-9E33-C80AA9429562:1

چون Replication ما از نوع GTID-based هست پس ما نیاز داریم که تنها statement هایی که می میتونند با استفاده از GTID با خیال راحت وارد سیستم بشن رو اعمال کنیم و باقی statement ها رو نادیده بگیریم. چه statement هایی با GTID-based replication سازگار نیستند؟ مثلا CREATE TABLE سازگار نیست.


gtid_mode=ON

حالا که بالا در مورد GTID و اجبارکردن سرور به اینکه حتما همه statementها از GTID استفاده کنند، صحبت کردیم و اعمالش کردیم، بهتره که سرور رو به حالت GTID ببریم. ممکنه شما یک سرور خام داشته باشید که هنوز روش داده ای نیست یا برعکس.

ببینید تقریبا تمام statementهایی که در mysql اجرا میکنید در یک تراکنش اجرا میشند. حالا تراکنش میتونه بر حسب اینکه چه position‌ی در فایل binlog داره شناسایی بشه یا هم GTID. پس وقتی سروری که از قبل روش داده هست رو به حالت GTID میبرید یا باید همه تراکنش ها GTID-based بشند یا فقط تراکنش های جدید GTID بگیرند.

اتفاقی که می افته اینه که برحسب مقداری که به این Flag میدید میتونه فقط تراکنش‌های جدید شما GTID بگیرند یا جدید و قدیم با هم GTID compatible باشند.


server_id=$RANDOM

خب ما اینجا دو تا عبارت برای توضیح دادن داریم. اول میریم سراغ server_id یا شناسه سرور. شناسه سرور همونطور که بالا گفتم عددی بین ۱ تا ۴ میلیارد و خورده ای هست. یعنی یک متغیر از نوع integer و unsigned هست. وقتی دارید چند سرور رو وارد cluster میکنید برای اینکه این سرورها از تفکیک بشن نیاز هست که اعداد مختلفی بهشون نسبت داده بشه.

ما چون از Docker برای راه اندازی cluster استفاده کردیم، تصمیم گرفتیم وقتی هر سروری راه‌اندازی میشه بهش با استفاده از متغیر RANDOM که یک متغیر سراسری در Bash لینوکس هست یک عدد تصادفی نسبت بدیم و چون تعداد سرورهای ما نهایتا ۷ خواهد بود پس احتمالا اینکه ۱ عدد تکراری رو به ۲ سرور نسبت داده باشیم خیلی کم هست.


max_connections=5000

حداکثر تعداد مجاز اتصال همزمان کلاینت ها.


open_files_limit=21010

در سیستم‌های Unix و Unix-like هر فایل برای بازشدن، بستن، نوشتن و خوانده شدن نیاز به یک عدد یکتا دارد. از آنجایی که فایل‌ها جزو منابع سیستم محسوب میشوند و هر پردازشی در یونیکس سقفی برای باز کردن منابع دارد، به صورت پیش‌فرض mysql تنها به ۱۰۲۴ اشاره‌گر فایل دسترسی دارد. در صورتی که فقط max_connection را اضافه کرده‌اید، عددش را در ۵ ضرب کرده و اینجا قرار دهید. اگر علاوه بر این table_open_cache رو هم تنظیم کرده با این فرمول عدد open_files_limit را محاسبه کنید.

۱۰ + max_connection + (table_open_cache * 2)


tmp_table_size=128M

حداکثر اندازه جداول موقتی حافظه داخلی. ببینید شما وقتی یک کوئری که مثلا توش join یا group by هست رو اجرا میکنید، mysql بعد از واکشی اطلاعات برای پردازش اونها یکسری جداول موقت که بعد از پایان یافتن کوئری از بین میرند، در RAM ایجاد میکنه. اگر اندازه این جداول کمتر از مقدار داده‌ای باشه که هر کوئری از دیسک فراخوانی میکنه، mysql مجبور میشه تا اطلاعات موقت رو دوباره رو دیسک بنویسه و در نتیجه I/O شما باز هم بالاتره میره.

برای مقداردهی به این flag بهتره ابتدا سرورهای خودتون رو زیر فشار قرار بدید، سپس کوئری show global status where Variable_name like '%tmp%' رو اجرا کنید. خروجی جدولی ۳ سطری خواهد بود که برای ما ۲ سطر Created_tmp_disk_tables و Created_tmp_tables مهم هستند. اگر RAM زیادی دارید بهتره مقدار tmp_table_size رو به قدری بالا ببرید تا Created_tmp_disk_tables به صفر برسه وگرنه هر چقدر ازین عدد کم کنید بهتره.


table_open_cache=8000

خب یکبار دیگه نیاز هست تا کوئری که بالا روی متغیرهای سیستمی اجرا کردیم رو اجرا کنیم. با این تفاوت که به جای %tmp% مقدار %Open_tables% رو قرار میدیم. اگر عددی که در سطر Open_tables هست خیلی بالا بود، بهتره مقدار این Flag رو بالاتر ببریم. البته حواستون باشه که اگر مقدار این flag رو بالا میبرید، باید مقدار open_files_limit رو هم بالا ببرید.


table_open_cache_instances=16 or 8

مستندات mysql توصیه میکنه که در صورتی که سرور شما پردازنده‌ای با ۱۶ هسته داره این مقدار رو روی ۸ یا ۱۶ بگذارید. بهتره بعد از اعمال هر کدوم از این عددها یکبار بنچمارک بگیرید تا عدد مناسب رو قرار بدید.


max_prepared_stmt_count=512000

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


transaction_isolation=REPEATABLE-READ

خب ما در RDBMS‌ها ۴ سطح قفل شدن داده داریم. این قفل شدن داده با قفل شدن‌های table lock و row lock که در InnoDB و MyISAM استفاده میشه فرق میکنه و نیاز داره که یک مقاله جداگانه در مورد انواع مقادیرش و نحوه عملکردشون نوشته بشه. پیشنهاد میشه در جاهایی که داده حساسی دارید از REPEATABLE-READ استفاده میکنید. در غیر این صورت میتونید سطح رو پایین تر بیارید مثلا روی READ-COMMITTED یا READ-UNCOMITTED


‍back_log=5000

خب تصور کنید که mysql شما در یک مدت زمان کوتاه، تعداد زیادی درخواست اتصال پیدا میکنه. مثلا یک کمپین تبلیغاتی برگزار کردید و انتظار دارید حجم زیادی کاربر محصول خاصی رو درخواست بدن و این تعداد از مقدار max_connection بیشتر بشه. در نتیجه اتصالات زیادی باید در صف قرار بگیرند. مقدار back_log نشون میده قبل از اینکه لحظه ای پاسخ MySQL به درخواست های جدید متوقف بشه چند درخواست رو می تونه در این مدت کوتاه انباشته کنه. البته در نظر داشته باشید که اگر انتظار تعداد اتصالات بسیار زیاد در زمان کم رو دارید این مقدار رو زیاد کنید. به عبارت دیگه، این مقدار به اندازه صف listen() برای ورودی های TCP / IP هستش. سیستم عامل شما محدودیت خاصی در اندازه این صف داره. اگر از سیستم عامل‌های Unix-like استفاده میکنید بهتره صفحه راهنمای سیستم عامل خودتون رو در این زمینه مطالعه کنید. چون این عدد نمیتونه بیشتر از اندازه‌ای که در سیستم عامل برای این صف در نظر گرفته شده، باشه.


character_set_server=utf8mb4

collation_server=utf8mb4_general_ci

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



mysqlmysql clusterinnodbinnodb cluster
سلام محمد هستم. توسعه دهنده بک‌اند ۲۹ ساله از خراسان که در تهران کار و زندگی میکنم و در اصفهان مشغول مطالعه هوش مصنوعی هستم.
شاید از این پست‌ها خوشتان بیاید