تعطّل قرص خادم متجر إلكتروني في بيروت في السادسة مساءً. النسخة الاحتياطية موجودة، لكنها من منتصف الليل، فضاعت طلبات 18 ساعة. لم تكن المشكلة في الأداء ولا في نوع قاعدة البيانات، بل في سؤال لم يطرحه أحد: كم من البيانات يحتمل العمل أن يخسر؟ هذا ما يسميه المهندسون (RPO). واستضافة قواعد البيانات تعني أن تجيب عنه وعن ثلاثة أسئلة غيره: أين تعمل القاعدة، وكم ذاكرة تحتاج، ومن يستطيع الاتصال بها. يتناول هذا الدليل MySQL وMariaDB وPostgreSQL بالأوامر، من التحجيم إلى تجربة الاستعادة.

01أين تشغّل قاعدة البيانات
ثلاثة خيارات، يفرّق بينها مقدار ما تتحكم به وما تتحمله:
| المعيار | قاعدة في الاستضافة المشتركة | خادم VPS تديره بنفسك | خدمة مُدارة |
|---|---|---|---|
| التحكم بالإعدادات | لا، فإعدادات الخادم مشتركة | كامل بصلاحيات root | جزئي، عبر لوحة المزود |
| الاستعادة إلى نقطة زمنية | غير متاحة عادة | متاحة إن فعّلتها | متاحة غالباً ضمن الخدمة |
| النسخ المتماثل والتحويل عند العطل | غير متاح | تبنيه بنفسك | ميزة جاهزة غالباً |
| العبء التشغيلي | منخفض | مرتفع: التحديث والمراقبة والنسخ عليك | منخفض، بتكلفة أعلى |
| يناسب | WordPress ومتجراً صغيراً وتطبيقات بقواعد صغيرة | تطبيقاً يحتاج ضبطاً أو أكثر من خادم | فريقاً بلا وقت للتشغيل ويحتاج توافراً عالياً |
ومهما اخترت، اجعل القاعدة قريبة من التطبيق. صفحة تنفّذ 80 استعلاماً متتالياً تخسر 160 ميلي ثانية فقط إن كان زمن الذهاب والإياب بين التطبيق والقاعدة 2 ميلي ثانية، لكنها تخسر قرابة 5 ثوانٍ إن كانت القاعدة في قارة أخرى بزمن 60 ميلي ثانية. لذلك يعمل التطبيق وقاعدته في مركز البيانات نفسه، وعبر شبكة خاصة إن كانا على خادمين.
02التحجيم: الذاكرة أولاً
قاعدة البيانات السريعة هي التي تجد بياناتها النشطة في الذاكرة. في MySQL وMariaDB يحدد ذلك بالمعامل innodb_buffer_pool_size، وفي PostgreSQL المعامل shared_buffers مع ذاكرة النظام المؤقتة. نقاط بداية عملية:
- MySQL وMariaDB: على خادم مخصص لقاعدة البيانات اجعل المجمع بين 60 و70% من الذاكرة. وإن شاركه خادم الويب وPHP فابدأ من 30 إلى 40%، فكل ما تأخذه القاعدة يُنتزع من عمليات PHP.
- PostgreSQL: shared_buffers نحو 25% من الذاكرة، وeffective_cache_size بين 50 و75%. ولا ترفع work_mem كثيراً، فقيمته تُحجز لكل عملية فرز في كل اتصال، فتتضاعف مع الاتصالات.
- الاتصالات: كل اتصال يحجز ذاكرة خاصة به، فلا ترفع max_connections إلى الآلاف. وفي PostgreSQL ضع أمام القاعدة مجمّع اتصالات مثل PgBouncer إن كان تطبيقك يفتح اتصالات كثيرة قصيرة.
وقِس حجم بياناتك الفعلي قبل أن تحجز الذاكرة. إن كان مجموع البيانات والفهارس 1.5 غيغابايت، فمجمع بحجم 2 إلى 3 غيغابايت يحفظها كلها في الذاكرة مع هامش للنمو، وما يزيد على ذلك لن يُستخدم حتى تكبر البيانات.
# حجم البيانات والفهارس لكل قاعدة، بالميغابايت (MySQL وMariaDB)sudo mariadb -e "SELECT table_schema, ROUND(SUM(data_length+index_length)/1024/1024) AS size_mb FROM information_schema.tables GROUP BY table_schema;"# إعداد MariaDB على خادم مخصص لقاعدة البيانات بذاكرة 8 GBsudo tee /etc/mysql/mariadb.conf.d/60-tuning.cnf >/dev/null <<'EOF'[mysqld]innodb_buffer_pool_size = 5Gmax_connections = 200slow_query_log = 1slow_query_log_file = /var/log/mysql/slow.loglong_query_time = 0.5EOFsudo systemctl restart mariadb
# PostgreSQL على خادم مخصص بذاكرة 8 GBsudo -u postgres psql -c "ALTER SYSTEM SET shared_buffers = '2GB';"sudo -u postgres psql -c "ALTER SYSTEM SET effective_cache_size = '6GB';"sudo -u postgres psql -c "ALTER SYSTEM SET work_mem = '16MB';"# shared_buffers لا يتغير إلا بعد إعادة التشغيلsudo systemctl restart postgresql
ثم التخزين. كل معاملة تُثبَّت تنتظر حتى يُكتب سجلها على القرص كتابة مؤكدة (fsync)، فزمن هذه الكتابة سقف لعدد المعاملات في الثانية. على NVMe محلي يكون عادة دون ميلي ثانية، وعلى تخزين شبكي قد يبلغ عدة ميلي ثوانٍ. قِسه قبل أن تضع قاعدة إنتاجية على أي خادم:
# زمن الكتابة المؤكدة: انظر إلى نسب fdatasync في المخرجات، وp99 دون 1 ms جيد لقاعدة بياناتfio --name=fsync-test --rw=write --bs=4k --size=256m --fdatasync=1 --ioengine=sync --runtime=30 --time_basedrm -f fsync-test.0.0
ولا تشترِ السرعة بالتخلي عن السلامة: ضبط innodb_flush_log_at_trx_commit على 2 يسرّع الكتابة، لكنه قد يضيّع آخر ثانية من المعاملات إن تعطل نظام التشغيل.
03النسخ الاحتياطي والاستعادة إلى نقطة زمنية
ثلاثة مستويات، يغطي كل منها ما لا يغطيه الآخر:
- نسخة منطقية: ملف SQL أو أرشيف من mariadb-dump أو mysqldump أو pg_dump. سهلة النقل بين الإصدارات والخوادم، وبطيئة الاستعادة حين تكبر القاعدة إلى عشرات الغيغابايت.
- نسخة فيزيائية: نسخة من ملفات القاعدة نفسها بأدوات مثل mariadb-backup أو Percona XtraBackup أو pg_basebackup. أسرع بكثير في استعادة القواعد الكبيرة، لكنها مرتبطة بالإصدار نفسه.
- سجل التغييرات: السجل الثنائي (binlog) في MySQL وMariaDB، و في PostgreSQL. مع نسخة كاملة يتيح لك ، كأن تعيد القاعدة إلى الدقيقة التي سبقت حذف جدول بالخطأ.
أما لقطة الخادم الكامل لقاعدة تعمل فليست بديلاً عن هذه المستويات: هي في أحسن الأحوال صورة لخادم انقطعت عنه الكهرباء فجأة، قد تعمل عند الاستعادة وقد لا تعمل، ولا تعطيك أي نقطة زمنية بين لقطتين. نشرح الفرق بين اللقطات والنسخ الاحتياطية في مقال مستقل على المدونة.

# نفّذ بصلاحيات root، ثم انقل النسخ إلى تخزين خارج الخادمmkdir -p /var/backups/db && chmod 700 /var/backups/db# MariaDB: نسخة متسقة دون قفل جداول InnoDB (مع MySQL استخدم mysqldump)mariadb-dump --single-transaction --routines --triggers --events shop | gzip > /var/backups/db/shop-$(date +%F).sql.gz# PostgreSQL: صيغة مخصصة مضغوطة تُستعاد كاملة أو جزئياًsudo -u postgres pg_dump -Fc shop > /var/backups/db/shop-$(date +%F).dump
والاستعادة إلى نقطة زمنية تسير هكذا: تفعّل السجل الثنائي مسبقاً، وتسجّل مع كل نسخة كاملة موقعها فيه، ثم تستعيد عند الحادث آخر نسخة، وتعيد تطبيق السجل من ذلك الموقع حتى الدقيقة التي تسبق الخطأ. وفي PostgreSQL تؤرشف ملفات WAL خارج الخادم، وتحدد وقت الهدف عند الاستعادة. أدوات مثل pgBackRest وWAL-G تتولى ذلك كله، وهي أسلم من السكربتات اليدوية.
# MariaDB: أضف إلى ملف الإعداد السابق ثم أعد التشغيل، فيُحفظ السجل الثنائي 7 أيام[mysqld]log_bin = mysql-binbinlog_format = ROWbinlog_expire_logs_seconds = 604800# بعدها أضف الخيار --master-data=2 إلى أمر mariadb-dump ليسجّل موقع البدء في كل نسخة# وعند الحادث: mariadb-binlog --stop-datetime يعيد التطبيق حتى الوقت الذي تحدده
والنسخة التي لم تُستعد قط مجرد افتراض. خصص كل شهر نصف ساعة لاستعادة آخر نسخة على قاعدة مؤقتة، وتحقق من عدد السجلات في أهم الجداول، وسجّل كم استغرقت الاستعادة: هذا الرقم هو ما ستخبر به إدارتك يوم العطل.
# بصلاحيات root. MariaDB: استعادة إلى قاعدة مؤقتة مع قياس الوقت، ثم فحص سريعmariadb -e "CREATE DATABASE shop_restore_test"time (gunzip -c /var/backups/db/shop-2026-10-06.sql.gz | mariadb shop_restore_test)mariadb -e "SELECT COUNT(*) FROM shop_restore_test.orders"# PostgreSQLsudo -u postgres createdb shop_restore_testtime sudo -u postgres pg_restore -d shop_restore_test < /var/backups/db/shop-2026-10-06.dump# بعد الفحص احذف القاعدتين المؤقتتين وحدهما، وتأكد من الاسم قبل التنفيذmariadb -e "DROP DATABASE shop_restore_test"sudo -u postgres dropdb shop_restore_test
04النسخ المتماثل: ما يحميك منه وما لا يحميك
يُبقي خادماً ثانياً يطبّق تغييرات الخادم الأساسي باستمرار: عبر السجل الثنائي في MySQL وMariaDB، وعبر بث WAL في PostgreSQL. يحميك من تعطل الخادم الأساسي، ويخفف عنه استعلامات القراءة الثقيلة كالتقارير. لكنه لا يحميك من الخطأ البشري: أمر حذف جدول يصل إلى النسخة الثانية خلال أجزاء من الثانية. فالنسخ المتماثل يكمّل النسخ الاحتياطي ولا يغني عنه.
وانتبه لأمرين. الأول أن النسخ غير المتزامن قد يتأخر ثوانٍ تحت الحمل، فما كُتب في الأساسي قبل العطل مباشرة قد لا يكون في النسخة. والثاني أن التحويل التلقائي عند العطل يحتاج ثلاث عقد على الأقل أو آلية تصويت، وإلا خاطرت بخادمين يظن كل منهما أنه الأساسي. أدوات مثل Patroni لـ PostgreSQL وGalera لـ MariaDB تحل ذلك، بتعقيد تشغيلي يستحق التخطيط. ولمعظم الشركات الصغيرة توازن جيد: خادم أساسي، ونسخة متماثلة غير متزامنة في مركز بيانات آخر، واستعادة إلى نقطة زمنية، وتحويل يدوي موثّق خطوة بخطوة.

05الأمان: من يتصل وبأي صلاحية
- الاستماع: اربط القاعدة بالعنوان 127.0.0.1 إن كان التطبيق على الخادم نفسه، أو بعنوان الشبكة الخاصة إن كان على خادم آخر. لا تتركها تستمع على 0.0.0.0 خلف كلمة مرور وحدها، فالمنفذان 3306 و5432 يُمسحان من الإنترنت باستمرار.
- جدار الحماية: اسمح بمنفذ القاعدة لعنوان خادم التطبيق وحده، وأغلقه أمام كل ما سواه.
- أقل الصلاحيات: مستخدم لكل تطبيق بصلاحيات القراءة والكتابة على قاعدته فقط، ومستخدم منفصل لترحيلات المخطط، ولا صلاحيات على كل القواعد إلا للمدير.
- التشفير: كل اتصال يعبر شبكة يمر عبر TLS: الخيار require_secure_transport في MySQL وMariaDB، وأسطر hostssl مع المصادقة scram-sha-256 في ملف pg_hba.conf في PostgreSQL.
- في الاستضافة المشتركة: إن فعّلت الوصول البعيد إلى MySQL من cPanel، فأضف عنواناً ثابتاً بعينه، لا علامة % التي تفتحه لكل عنوان.
# من يستمع على منافذ قواعد البيانات؟ المطلوب 127.0.0.1 أو عنوان خاص، لا 0.0.0.0sudo ss -ltnp | grep -E ':(3306|5432) '# مستخدم للتطبيق على قاعدة shop وحدها، ومن عنوان خادم التطبيق فقطsudo mariadb -e "CREATE USER 'shop_app'@'198.51.100.12' IDENTIFIED BY 'Replace-With-A-Long-Random-Password'; GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'shop_app'@'198.51.100.12';"# جدار الحماية: منفذ MariaDB لخادم التطبيق وحدهsudo ufw allow from 198.51.100.12 to any port 3306 proto tcp
06المراقبة: الاستعلام البطيء قبل شكوى العميل
معظم بطء قواعد البيانات يأتي من استعلامات قليلة بلا فهارس مناسبة. لا تخمّن، بل سجّلها: في MySQL وMariaDB فعّلنا سجل الاستعلامات البطيئة في ملف الإعداد أعلاه بعتبة نصف ثانية، وفي PostgreSQL تجمع الإضافة pg_stat_statements إحصاءات كل استعلام:
# MariaDB: أثقل 5 استعلامات بمجموع الوقت في السجل البطيء (أو mariadb-dumpslow)sudo mysqldumpslow -s t -t 5 /var/log/mysql/slow.log# PostgreSQL: حمّل الإضافة ثم أعد التشغيل (إن كانت في القيمة مكتبات أخرى فأبقها معها)sudo -u postgres psql -c "ALTER SYSTEM SET shared_preload_libraries = 'pg_stat_statements';"sudo systemctl restart postgresqlsudo -u postgres psql -d shop -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;"# أثقل 5 استعلامات بمجموع وقت التنفيذsudo -u postgres psql -d shop -c "SELECT calls, round(total_exec_time) AS total_ms, round(mean_exec_time::numeric, 1) AS mean_ms, left(query, 80) AS query FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;"
وراقب باستمرار خمسة مؤشرات: الاتصالات المستخدمة مقارنة بالحد الأقصى، ونسبة القراءة من الذاكرة لا من القرص، وتأخر النسخة المتماثلة، والمساحة الحرة على القرص، ونجاح آخر نسخة احتياطية. أدوات مثل Percona Monitoring and Management أو Prometheus مع Grafana تجمعها في لوحة واحدة مع تنبيهات.
- المشكلةخطأ Too many connections
- الحلالتطبيق يفتح اتصالات ولا يغلقها، أو الطلبات بطيئة فتتراكم اتصالاتها. راجع الاستعلامات البطيئة، وضع مجمّع اتصالات أمام القاعدة، ثم ارفع الحد بقدر ما تسمح الذاكرة.
- المشكلةالقاعدة بطيئة والمعالج هادئ
- الحلانظر إلى انتظار القرص ونسبة القراءة من الذاكرة: الأرجح أن المجمع أصغر من البيانات النشطة.
- المشكلةامتلأ القرص فجأة مع أن البيانات لم تكبر
- الحلالسجل الثنائي أو ملفات WAL تتراكم. تحقق من مدة الاحتفاظ، ومن أن أرشفة WAL أو النسخة المتماثلة لم تتوقف، فـ PostgreSQL يحتفظ بالملفات حتى تُستهلك.
07قبل أن تقرر
ابدأ من سؤال الخسارة لا من سؤال الأداء: كم دقيقة من البيانات تحتمل أن تخسر، وكم ساعة توقف؟ إن كان الجواب «يوماً كاملاً» فنسخة ليلية موثوقة تكفي. وإن كان «دقائق» فأنت تحتاج سجل تغييرات مؤرشفاً خارج الخادم، أي خادماً تتحكم بإعداداته.
في إليفانتي، تضم استضافة cPanel قواعد MariaDB المتوافقة مع MySQL وقواعد PostgreSQL بعدد غير محدود، مع phpMyAdmin والوصول البعيد إلى MySQL، وتدخل قواعد البيانات في نسخ JetBackup اليومية. هذا يكفي لمعظم مواقع WordPress والمتاجر الصغيرة. وحين تحتاج ضبط الذاكرة أو الاستعادة إلى نقطة زمنية أو نسخة متماثلة، فخادم CoreVPS يمنحك صلاحيات root على معالجات AMD EPYC وذاكرة DDR5 ECC وتخزين NVMe بمصفوفة RAID 10، مع MySQL وMongoDB تطبيقين جاهزين عند الطلب، وPercona Monitoring وGrafana للمراقبة. شغّل اختبار fio في يومك الأول، وجرّب استعادة كاملة قبل أن تنقل أول بيانات حقيقية.




