لماذا تستضيف MySQL ذاتيًا على خادم VPS
على الاستضافة المشتركة، تتقاسم قاعدة بيانات MySQL الخاصة بك ذاكرة RAM وعمليات الإدخال/الإخراج مع مئات المواقع الأخرى، ولا يمكنك تعديل innodb_buffer_pool_size ولا أوضاع SQL ولا إصدار المحرك. على خادم VPS، تحصل على موارد مخصصة، وتختار بين MySQL 8.4 LTS وMariaDB، وتصل إلى ملف my.cnf لتحسين كل معامل. إنه الخيار الصحيح لمتجر تجارة إلكترونية، أو نظام إدارة محتوى عالي الحركة، أو واجهة برمجية (API) تحتاج إلى معاملات سريعة وbuffer pool سخي. كما تدير المستخدمين الخاصين بك، وصلاحيات المنح (grants) على مستوى الجداول، واستراتيجية النسخ المتماثل بين الخادم الرئيسي والتابع.
الفوائد الملموسة لـ MySQL مُستضاف ذاتيًا
- موارد مخصصة: كامل buffer pool الخاص بـ InnoDB لبياناتك، دون جيران مزعجين.
- وصول كامل إلى
my.cnf:innodb_buffer_pool_sizeوmax_connectionsوأوضاع SQL الصارمة. - اختيار المحرك: MySQL 8.4 LTS للاستقرار طويل الأمد، أو MariaDB لمحركاتها الإضافية.
- نسخ متماثل بين الخادم الرئيسي والتابع قابل للتهيئة لتوزيع عمليات القراءة أو التعافي من الأعطال.
- نسخ احتياطية منطقية (
mysqldump) أو فيزيائية (xtrabackup) حسب حجم بياناتك. - لا حصص تعسفية على حجم قواعد البيانات أو عدد الاتصالات المتزامنة.
المتطلبات الأساسية قبل البدء
الإصدار الموصى به: MySQL 8.4 LTS (الإصدار الحالي: 8.4.11)، الفرع الرسمي للدعم طويل الأمد، مدعوم حتى عام 2032. تجنّب MySQL 8.0 في التثبيتات الجديدة: ينتهي دعمه النشط في 2025.
ذاكرة RAM: 1 GB حدًّا أدنى لبيئة الاختبار أو مدونة خفيفة. خطّط لـ 2 GB لموقع WordPress أو تطبيق صغير في الإنتاج، و4 إلى 8 GB لـ Magento أو PrestaShop أو واجهة برمجية عالية الحركة. القاعدة الأساسية: خصّص 60 إلى 70% من RAM المتاحة لـ innodb_buffer_pool_size.
المعالج: 2 vCPU تكفي لمعظم الحالات. ارتقِ إلى 4 vCPU عند كثرة الاستعلامات أو تفعيل النسخ المتماثل.
التخزين: 30 GB من SSD NVMe كنقطة انطلاق؛ احتفظ بمساحة إضافية لملفات binlogs إن فعّلت النسخ المتماثل أو الاستعادة إلى نقطة زمنية محددة.
المنافذ: يستمع MySQL افتراضيًا على المنفذ 3306. يجب ألّا يكون هذا المنفذ مفتوحًا على الواجهة العامة. يجب أن يُشير المعامل bind-address في my.cnf إلى 127.0.0.1 أو عنوان الشبكة الخاصة، لا إلى 0.0.0.0.
النظام: Ubuntu 22.04 أو 24.04 LTS، وDocker وDocker Compose v2، ووحدة تخزين دائمة مخصصة.
نشر MySQL 8.4 باستخدام Docker في بيئة الإنتاج
تأمين خادم VPS
حدّث النظام (
apt update && apt upgrade -y)، وثبّت Docker، وعطّل الدخول عبر SSH بكلمة المرور لصالح المفاتيح (PasswordAuthentication noفيsshd_config)، وأغلق المنفذ 3306 نحو الخارج:ufw deny 3306 ufw allow OpenSSH ufw enableيجب ألّا يستمع MySQL إلا على الشبكة الداخلية لحاوية Docker.
تعريف ملف docker-compose.yml
أعلن عن خدمة
mysql:8.4، وركّب وحدة تخزين على/var/lib/mysql، ومرّر متغيرات البيئة عبر ملف.env:# .env MYSQL_ROOT_PASSWORD=كلمة_مرور_root_قوية MYSQL_DATABASE=قاعدة_بياناتي MYSQL_USER=app_user MYSQL_PASSWORD=كلمة_مرور_التطبيق# docker-compose.yml services: mysql: image: mysql:8.4 restart: unless-stopped env_file: .env volumes: - mysql_data:/var/lib/mysql - ./conf.d:/etc/mysql/conf.d:ro ports: - "127.0.0.1:3306:3306" volumes: mysql_data:لاحظ الربط بـ
127.0.0.1فيports: لن يكون المنفذ متاحًا إلا من الخادم نفسه.إنشاء قاعدة بيانات ومستخدم مخصص
بعد
docker compose up -d، اتصل بالحاوية:docker exec -it mysql mysql -u root -pأنشئ قاعدة البيانات ومستخدم التطبيق:
CREATE DATABASE قاعدة_بياناتي CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'app_user'@'%' IDENTIFIED BY 'كلمة_مرور_التطبيق'; GRANT SELECT, INSERT, UPDATE, DELETE ON قاعدة_بياناتي.* TO 'app_user'@'%'; FLUSH PRIVILEGES;حصّر الصلاحيات في الحد الأدنى الضروري: لا تستخدم
GRANT ALL PRIVILEGESأبدًا لحساب التطبيق.تعزيز أمان التثبيت
نفّذ ما يعادل
mysql_secure_installationيدويًا:-- حذف المستخدمين المجهولين DELETE FROM mysql.user WHERE User=''; -- منع تسجيل دخول root عن بُعد DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1'); -- حذف قاعدة البيانات التجريبية DROP DATABASE IF EXISTS test; FLUSH PRIVILEGES;لا تستخدم حساب
rootأبدًا من تطبيقك. تحقق من وجودbind-address = 127.0.0.1في ملفconf.d/custom.cnf.الكشف عبر TLS عند الحاجة
إذا كان على تطبيق خارجي الاتصال عن بُعد، لا تعرّض المنفذ 3306 مباشرة على الإنترنت. استخدم نفق SSH:
ssh -L 3306:127.0.0.1:3306 [email protected] -Nثم اتصل بعميل MySQL على
127.0.0.1:3306— يمر التراسل مشفرًا عبر SSH. للاتصال الدائم بين خوادم VPS، هيّئ MySQL للاستماع على واجهة الشبكة الخاصة فقط واسمح فقط لعنوان IP الخادم التطبيقي في UFW.جدولة النسخ الاحتياطية
جدوِل
mysqldump --single-transactionيوميًا عبر cron للحصول على نسخ متسقة دون قفل:# /etc/cron.d/mysql-backup 0 2 * * * root docker exec mysql mysqldump -u root -p"${MYSQL_ROOT_PASSWORD}" --single-transaction --all-databases | gzip > /backups/mysql-$(date +\%Y\%m\%d).sql.gzاحتفظ بما لا يقل عن 7 أيام من الاحتفاظ، وأرشف النسخ خارج خادم VPS. اختبر الاستعادة مرة شهريًا: نسخة احتياطية غير مختبَرة ليست نسخة احتياطية. للقواعد الكبيرة، انتقل إلى Percona XtraBackup.
التأمين: الإجراءات الأساسية
mysql_secure_installation في حاوية. لا تُنفّذ الصورة الرسمية لـ Docker هذا الأمر تلقائيًا: نفّذه يدويًا بعد أول تشغيل (الخطوة 4 أعلاه).
حذف root البعيد. يجب أن يكون حساب root متاحًا من localhost فقط. حساب root متاح من % هو أول ثغرة يستغلها فحص المنافذ.
مستخدم مخصص لكل تطبيق. يحصل كل تطبيق متصل على حسابه الخاص في MySQL بصلاحيات مقيدة بقاعدة بياناته. تطبيق معطوب لن يتمكن من قراءة أو تعديل بيانات التطبيقات الأخرى.
UFW + bind-address. الحماية المزدوجة إلزامية: ufw deny 3306 على مستوى جدار الحماية النظامي، وbind-address = 127.0.0.1 في my.cnf من جانب MySQL. لا يُغني أحدهما عن الآخر.
كلمات مرور قوية. استخدم caching_sha2_password (الإضافة الافتراضية في MySQL 8.x) وكلمات مرور مُولَّدة (20 حرفًا على الأقل). خزّنها في أسرار مُدارة، لا في الشفرة المصدرية أبدًا.
تحسين أداء InnoDB
تُضاف هذه المعاملات في ملف conf.d/custom.cnf المركَّب بوضع القراءة فقط في الحاوية.
innodb_buffer_pool_size: المعامل الأهم. اضبطه على 60–70% من RAM خادم VPS. على خادم بـ 4 GB، ذلك يعني 2.5 إلى 2.8 GB. صغير جدًا = قراءات قرص متكررة؛ كبير جدًا = مبادلة (swap).
innodb_flush_log_at_trx_commit: القيمة 2 تُقدّم توازنًا جيدًا بين الأداء والمتانة لمعظم التطبيقات. القيمة 1 (الافتراضية) أكثر أمانًا لكنها أبطأ؛ القيمة 0 هي الأسرع لكنها محفوفة بالمخاطر عند الأعطال.
max_connections: اضبطه وفق حملك الفعلي. القيمة الافتراضية (151) غالبًا منخفضة جدًا للتطبيقات عالية الحركة. كل اتصال يستهلك ~1 MB من RAM.
slow_query_log: فعّله بـ slow_query_log = 1 وlong_query_time = 1 لالتقاط كل الاستعلامات التي تتجاوز ثانية واحدة. هو أداتك الأولى للتحسين.
مثال على custom.cnf لخادم VPS بـ 4 GB:
[mysqld]
innodb_buffer_pool_size = 2G
innodb_flush_log_at_trx_commit = 2
max_connections = 200
slow_query_log = 1
long_query_time = 1
log_bin = /var/lib/mysql/binlog
binlog_format = ROWالوصول البعيد الآمن: نفق SSH
تعريض المنفذ 3306 على الواجهة العامة لخادم VPS خطأ أمني جسيم: تحاول الماسحات التلقائية هجمات القوة الغاشمة على هذا المنفذ باستمرار. النهج الصحيح هو نفق SSH.
اتصال لمرة واحدة من جهازك:
ssh -L 3306:127.0.0.1:3306 [email protected] -Nافتح بعد ذلك عميل MySQL على 127.0.0.1:3306 — يمر التراسل مشفرًا عبر SSH.
اتصال دائم بين خوادم VPS: إذا كان تطبيقك يعمل على خادم VPS ثانٍ على نفس الشبكة الخاصة، هيّئ MySQL للاستماع على عنوان الشبكة الخاصة فقط (bind-address = 10.0.0.X)، واسمح فقط لعنوان IP الخادم التطبيقي في UFW:
ufw allow from 10.0.0.5 to any port 3306ما يجب تجنّبه: bind-address = 0.0.0.0 دون جدار حماية، المنفذ 3306 مفتوح علنًا، أو حسابات MySQL بـ Host='%' دون قيود IP.
النسخ الاحتياطية التلقائية: استراتيجية شاملة
تعتمد استراتيجية النسخ الاحتياطي لـ MySQL على مستويين متكاملين.
المستوى الأول — نسخ منطقية يومية (mysqldump): متسقة بفضل --single-transaction (لا قفل قراءة على InnoDB)، قابلة للنقل بين الإصدارات. مناسبة للقواعد حتى عشرات الـ GB.
المستوى الثاني — binlogs للاستعادة إلى نقطة زمنية: فعّل log_bin منذ البداية. بدمجها مع نسخة كاملة أسبوعية، تتيح لك binlogs إعادة تشغيل كل معاملة حتى الثانية التي سبقت حادثة.
للقواعد الكبيرة — Percona XtraBackup: نسخ فيزيائية دون انقطاع الخدمة، استعادة سريعة (نسخ ملفات لا إعادة تنفيذ SQL).
الاحتفاظ والأرشفة: لا تخزّن النسخ على خادم VPS فقط. خادم VPS معطوب يُضيّع نسخه الاحتياطية معه. صدّر إلى تخزين كائنات أو خادم SFTP بعيد، واحتفظ بـ 7 أيام على الأقل.
اختبار الاستعادة: نسخة غير مختبَرة ليست نسخة احتياطية. خطّط لاختبار شهري: استعِد على حاوية مؤقتة وتحقق من سلامة بياناتك.
# استعادة من نسخة مضغوطة
zcat /backups/mysql-20260917.sql.gz | docker exec -i mysql mysql -u root -pاستكشاف الأخطاء: 5 أخطاء شائعة
Access denied for user 'app'@'...'
الحساب موجود لكن Host لا يتطابق. يتحقق MySQL من عنوان IP المصدر بدقة. إذا اتصل التطبيق من حاوية Docker، قد يكون عنوان IP المصدر 172.17.0.x لا localhost. تحقق بـ:
SELECT User, Host FROM mysql.user;الحل: أنشئ الحساب بـ 'app_user'@'%' (أو عنوان IP الحاوية بالضبط) وأعد تشغيل FLUSH PRIVILEGES.
Too many connections
تم الوصول إلى max_connections. تحقق من عدد الاتصالات النشطة:
SHOW STATUS LIKE 'Threads_connected';زِد max_connections في custom.cnf وتحقق من أن تطبيقك يستخدم مجمع اتصالات (connection pool).
Table '...' is marked as crashed
تلف InnoDB، في الغالب بعد إيقاف مفاجئ. حاول الإصلاح:
mysqlcheck -u root -p --auto-repair --all-databasesإن تعذّر إصلاح الجدول، استعِد من آخر نسخة احتياطية متسقة.
Can't connect to MySQL server on '127.0.0.1' (111)
MySQL لا يستمع على الواجهة المتوقعة. تحقق من bind-address في custom.cnf وأعد تشغيل الحاوية. تحقق أيضًا من أن الحاوية تعمل بـ docker compose ps.
ERROR 1819 (HY000): Your password does not satisfy the current policy
إضافة التحقق من كلمة المرور في MySQL 8 ترفض كلمات المرور البسيطة. استخدم كلمة مرور بـ 8 أحرف على الأقل مع أحرف كبيرة وأرقام ورموز، أو اضبط validate_password.policy = LOW في custom.cnf لبيئات التطوير فقط.
فعّل ملفات binlogs (log_bin + binlog_format=ROW) منذ البداية: فهي مفتاح الاستعادة إلى نقطة زمنية محددة. وبدمجها مع mysqldump كامل أسبوعي، تتيح لك إعادة تشغيل جميع المعاملات حتى الثانية التي سبقت العطل. وإذا أنشأت تابعًا (replica) للنسخ المتماثل على خادم VPS ثانٍ، فإن ملفات binlogs نفسها تُغذّي النسخة التابعة في الوقت الفعلي لعمليات القراءة الموزّعة.