إدارة قواعد البيانات: MariaDB وMySQL وPostgreSQL في بيئة الإنتاج
خروج

الدرس 1 من 12 · التثبيت والإعداد

MariaDB وMySQL في بيئة الإنتاج: التثبيت، والملفات، وmy.cnf

55 د Debian 13 الإصدارات MariaDB 10.11, MariaDB 11.4, MariaDB 11.8, MySQL 8.4

الهدف

تثبيت خادم MariaDB أو MySQL كما يُفعل في بيئة الإنتاج: الإصدار الصحيح، والمستودع الصحيح، وإعدادات في ملفاتك الخاصة، ونظام مهيّأ لقاعدة بيانات، واليقين من معرفة القيمة التي اعتمدها الخادم فعلًا.

أهم النقاط

  • قاعدة بيانات الإنتاج هي دورة حياة: نختار إصدارًا LTS (دعم طويل الأمد) ونتابع إصداراته الفرعية التصحيحية.
  • الملفات التي يجب معرفتها: مجلد البيانات (datadir) (/var/lib/mysql)، والمقبس (socket) (/run/mysqld/mysqld.sock)، وسجل الأخطاء، وملفات my.cnf.
  • يُقرأ my.cnf بترتيب محدّد، مع ملفات مُضمَّنة؛ وبالنسبة لخيار مكرّر، آخر قيمة هي التي تُعتمد.
  • لا نعدّل أبدًا ملفات الحزمة: نضيف ملفنا الخاص (90-production.cnf) ونتحقّق من القيمة الفعلية.
  • يُضبط الخادم أيضًا حول القاعدة: حدود systemd، والـ swap، ووحدة تخزين مخصّصة مركّبة بخيار noatime.

1. المهنة: ما يضيفه مدير قواعد البيانات (DBA) إلى « apt install »

أرتكم دروس Debian وRed Hat وUbuntu كيف تثبّتون MariaDB، وتنشئون قاعدة بيانات وحسابًا. هنا نأخذ مكان الشخص الذي يتحمّل مسؤولية القاعدة: يجب أن تصمد سنوات، وأن تتحمّل ضغط متجر MasterLinux ليلة التخفيضات، وأن تُنسخ احتياطيًا، وتُستنسخ، وتُراقب، وتُحدَّث دون ضياع.

أسئلة يجب طرحها قبل التثبيت:

السؤال لماذا
أي إصدار، ومدعوم إلى متى؟ تنشر MariaDB إصدارات LTS (10.11، 11.4، 11.8…) تُصان لمدة 5 سنوات تقريبًا؛ ولـ MySQL إصدارها LTS 8.4 وإصدارات « Innovation » قصيرة العمر.
حزمة التوزيعة أم المستودع الرسمي؟ يوفّر Debian 13 الإصدار MariaDB 11.8؛ أما مستودع MariaDB أو MySQL فيعطي إصدارًا محدّدًا وتحديثات أسرع. نختار مصدرًا واحدًا ونلتزم به.
محرّك التخزين؟ InnoDB لكل شيء (المعاملات، والاستئناف بعد العطل). لا مكان لـ MyISAM في الإنتاج.
أي جهاز؟ الذاكرة RAM (ذاكرة InnoDB المؤقتة)، وأقراص SSD/NVMe، ووحدة تخزين مخصّصة للبيانات.

2. التثبيت وتحديد مواقع الملفات

apt install mariadb-server mariadb-backup        # Debian / Ubuntu
dnf install mariadb-server mariadb-backup        # Rocky / Alma (MySQL : dnf install mysql-server)

systemctl enable --now mariadb
mariadb -e "SELECT VERSION(), @@datadir, @@socket, @@log_error;"
العنصر Debian / Ubuntu Rocky / Alma
الملف الرئيسي /etc/mysql/my.cnf → mariadb.cnf /etc/my.cnf
المجلدات المُضمَّنة /etc/mysql/conf.d/، /etc/mysql/mariadb.conf.d/ /etc/my.cnf.d/
البيانات (datadir) /var/lib/mysql /var/lib/mysql
سجل الأخطاء سجل systemd (journalctl -u mariadb) أو /var/log/mysql/error.log /var/log/mariadb/mariadb.log
اسم الخدمة mariadb (الاسم البديل mysql) mariadb أو mysqld

أُعيدت تسمية الأوامر: mariadb (العميل)، وmariadb-dump، وmariadb-admin، وmariadb-backup، وmariadb-binlog. ما زالت الأسماء القديمة mysql* موجودة كروابط على أنظمة كثيرة، لكنها تختفي شيئًا فشيئًا.

3. كيف يُقرأ my.cnf

على Debian، يبدو /etc/mysql/mariadb.cnf هكذا:

[client-server]
socket = /run/mysqld/mysqld.sock

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mariadb.conf.d/

ثلاث قواعد يجب حفظها جيدًا:

  1. الأقسام (المجموعات) تحدّد من يقرأ الخيار: [mysqld]، و[server]، و[mariadb]، و[mariadb-11.8] (لهذا الإصدار فقط) بالنسبة للخادم؛ و[client]، و[mysql]، و[mariadb-dump] بالنسبة للأدوات.
  2. !includedir يقرأ ملفات المجلد بالترتيب الأبجدي، ولا يقرأ إلا تلك التي تنتهي بـ .cnf.
  3. بالنسبة لخيار مكرّر، آخر ما يُقرأ هو الذي يُعتمد. ملف 99-vieux.cnf منسيّ يتغلّب على ملفك 90-production.cnf.

لمعرفة ما سيقرؤه الخادم، دون إعادة تشغيله:

my_print_defaults --mysqld          # كل خيارات الخادم، بترتيب القراءة
mariadbd --print-defaults           # الشيء نفسه، كما يراه الملف التنفيذي للخادم
mariadb -e "SELECT @@innodb_buffer_pool_size/1024/1024/1024 AS go, @@max_connections;"   # القيمة الجارية حاليًا

في التطبيق العملي، يعطي الأمر cnf-effectif <option> القيمة النهائية التي سيأخذها الخادم عند الإقلاع (ما يعادل SELECT @@option).

المتغيّرات الديناميكية والثابتة

جزء من الإعدادات يمكن تغييره أثناء التشغيل:

SET GLOBAL max_connections = 300;            -- فوري، لكنه يضيع عند إعادة التشغيل
SET GLOBAL innodb_buffer_pool_size = 10*1024*1024*1024;   -- تغيير الحجم أثناء التشغيل (InnoDB)

وأخرى تتطلّب إعادة تشغيل (innodb_log_file_size في الإصدارات القديمة، وbind_address…). قاعدة تشغيلية: كل تغيير أثناء التشغيل يُكتب أيضًا في الملف، وإلا اختفى عند إعادة التشغيل التالية. يقترح MySQL 8 الأمر SET PERSIST، الذي يكتب في mysqld-auto.cnf داخل مجلد البيانات؛ هذا عملي، لكن يجب أن يكون هذا الملف معروفًا لدى الفريق كله، وإلا فلن يفهم أحد من أين تأتي قيمة ما.

4. الإعدادات المهمّة لخادم مخصّص

# /etc/mysql/mariadb.conf.d/90-production.cnf — ml-db-01, 16 Go de RAM, dédié
[mysqld]
bind_address                   = 10.10.20.11   # عنوان الشبكة الخاصة، وليس 0.0.0.0 أبدًا دون جدار ناري
skip_name_resolve              = ON            # لا DNS عكسي عند كل اتصال (حسابات حسب IP)
max_connections                = 300

innodb_buffer_pool_size        = 10G           # من 60 إلى 70 % من ذاكرة خادم مخصّص
innodb_log_file_size           = 2G            # سجل redo: كبير بما يكفي لتسوية عمليات الكتابة
innodb_flush_log_at_trx_commit = 1             # 1 = لا تضيع أي معاملة مؤكَّدة في حالة العطل
innodb_file_per_table          = ON            # ملف لكل جدول (افتراضي)
  • الـ Buffer pool (المخزن المؤقت): ذاكرة صفحات InnoDB المؤقتة. إذا كان صغيرًا جدًا، يذهب كل شيء إلى القرص؛ وإذا كان كبيرًا جدًا، يلجأ الخادم إلى الـ swap أو يقتله الـ OOM killer. نترك ذاكرة للنظام، وللاتصالات (كل واحد منها يستهلك مخازن مؤقتة)، ولذاكرة الملفات المؤقتة.
  • innodb_flush_log_at_trx_commit: القيمة 1 هي الإعداد الوحيد الذي يضمن الديمومة. القيمتان 2 أو 0 أسرع لكن قد تُضيعان آخر ثانية من المعاملات عند انقطاع التيار: تُخصَّصان لقاعدة يمكن إعادة بنائها.
  • مجموعة المحارف: utf8mb4 في كل مكان (الترميز القديم utf8 في MySQL لا يخزّن إلا 3 بايت ويُفسد الرموز التعبيرية emojis وبعض المحارف).

5. تهيئة النظام

# /etc/systemd/system/mariadb.service.d/limites.conf
[Service]
LimitNOFILE=65535        # الجدول الواحد = ملفات مفتوحة؛ القيمة الافتراضية 1 024 لا تكفي
TimeoutStopSec=900       # منح الوقت لتفريغ buffer pool كبير عند الإيقاف
systemctl daemon-reload && systemctl restart mariadb
cat /proc/$(pidof mariadbd)/limits | grep 'open files'

إذا قتل systemd الخادم بعد 90 ثانية أثناء الإيقاف، تُعاد القاعدة في وضع الاستعادة بعد العطل (crash recovery): أطول، ومقلق في ليلة صيانة.

echo 'vm.swappiness = 1' > /etc/sysctl.d/60-mariadb.conf && sysctl --system   # الـ swap كملاذ أخير

مجلد البيانات على وحدة تخزين مخصّصة (LVM): لا تملأ القاعدة /، ونكبّر الوحدة دون لمس النظام، ويمكن أخذ لقطة (snapshot). يتجنّب noatime عملية كتابة عند كل قراءة لملف.

/dev/vg_bdd/lv_mysql  /var/lib/mysql  xfs  defaults,noatime  0 2

حسب الإصدار

النقطة MariaDB 10.11 MariaDB 11.4 / 11.8 MySQL 8.4 LTS
اسم الملف التنفيذي للخادم mariadbd (mysqld كرابط) mariadbd mysqld
الترتيب (collation) الافتراضي لـ utf8mb4 utf8mb4_general_ci uca1400_ai_ci (Debian 13) utf8mb4_0900_ai_ci
تغيير innodb_log_file_size أثناء التشغيل منذ 10.11 أثناء التشغيل innodb_redo_log_capacity أثناء التشغيل
إعدادات دائمة أثناء التشغيل لا (اكتب في الملف) لا SET PERSIST (mysqld-auto.cnf)
TLS للاتصالات يجب إعداده مفعّل افتراضيًا منذ 11.4 مفعّل افتراضيًا (شهادات مولَّدة)

أخطاء شائعة

  • تعديل 50-server.cnf: يُستبدل أو يثير سؤالًا عند كل تحديث للحزمة. ملفك 90-… يبقى ملكك.
  • ملف قديم يتغلّب: ملف 99-…cnf أو zz-…cnf من مزوّد خدمة سابق يُلغي إعداداتك. تحقّق دائمًا من القيمة الفعلية.
  • إعادة التسمية بـ .bak في الاتجاه الخاطئ: fichier.cnf.bak لم يعد يُقرأ (جيد)، لكن fichier.bak.cnf ما زال يُقرأ.
  • Buffer pool بنسبة 90 % من الذاكرة على خادم يشغّل أيضًا PHP أو Java: تقتل النواة mariadbd في منتصف النهار.

حالة واقعية

في مساء يوم جمعة، صار متجر MasterLinux بطيئًا جدًا. مع أن الخادم يملك 16 Go من الذاكرة، وقد « ضبط الفريق الـ buffer pool على 10 Go » في 90-production.cnf قبل ستة أشهر. يجيب SELECT @@innodb_buffer_pool_size… 256 Mo. كان ملف 99-optimisation-2023.cnf، المنسوخ من خادم قديم، يُقرأ في الأخير. كانت مجموعة البيانات كلها تمرّ عبر القرص. استغرق الإصلاح دقيقتين؛ أما الدرس فيبقى: نتحقّق من القيمة الفعلية بعد كل تغيير.

التطبيق العملي

على جهاز Debian 13 يحاكي شجرة ملفات MariaDB 11.8، ستقوم بما يلي:

  1. كتابة ملف إعدادات الإنتاج الخاص بك؛
  2. اكتشاف لماذا لن يعتمدها الخادم، وتصحيح ذلك دون حذف أي شيء؛
  3. إضافة ملف التجاوز الخاص بـ systemd؛
  4. ضبط الـ swap؛
  5. التصريح بوحدة التخزين المخصّصة للبيانات في /etc/fstab.

يُظهر لك الأمران my_print_defaults --mysqld وcnf-effectif <option> ما سيقرؤه الخادم.

اختبار

  1. داخل مجلد مُضمَّن بواسطة !includedir، بأي ترتيب تُقرأ الملفات، وأيّها يُقرأ؟
  2. ملفّان يحدّدان max_connections: أيّهما يُعتمد؟
  3. لماذا innodb_flush_log_at_trx_commit = 1 في الإنتاج؟
  4. ما نسبة الذاكرة التي تُعطى للـ buffer pool على خادم مخصّص، ولماذا ليس أكثر؟
  5. ماذا يحدث إذا قتل systemd خادم MariaDB أثناء إيقاف طويل جدًا؟
الأجوبة
  1. بالترتيب الأبجدي، ولا تُقرأ إلا الملفات التي تنتهي بـ .cnf.
  2. الملف الذي يُقرأ في الأخير.
  3. لأنه الإعداد الوحيد الذي يكتب سجل redo على القرص عند كل تأكيد: لا تضيع أي معاملة مؤكَّدة في حالة العطل.
  4. حوالي 60 إلى 70 %: يجب ترك ذاكرة للنظام، وللاتصالات، ولذاكرة الملفات المؤقتة، وإلا حدث اللجوء إلى الـ swap أو تدخّل الـ OOM killer.
  5. يُعاد تشغيل الخادم في وضع الاستعادة بعد العطل (crash recovery)، وهي أطول، مع خطر أكبر؛ ومن هنا تأتي ضرورة إطالة TimeoutStopSec.

تمارين تطبيقية

تُنجَز على Debian 13

أنجز هذا التطبيق العملي على جهازك

الطرفية طرفيتك: آلة افتراضية بنظام لينكس (VirtualBox أو VMware أو Hyper-V أو WSL…) أو Docker/Podman. أداة ml تحضّر التمرين وتصحّح عملك، ويُؤكَّد الدرس هنا تلقائيًا.

أنشئ حسابًا مجانيًا للحصول على أمرك الشخصي وتصحيح تطبيقاتك العملية تلقائيًا.

إنشاء حسابي

المهام المطلوبة

  1. يجب أن يتلقّى الخادم ml-db-01 (ذاكرة 16 Go، مخصّص لـ MariaDB) إعداداتك في ملف خاص بك: أنشئ /etc/mysql/mariadb.conf.d/90-production.cnf بقسم [mysqld] يحتوي على innodb_buffer_pool_size = 10G وinnodb_log_file_size = 2G وinnodb_flush_log_at_trx_commit = 1 وmax_connections = 300 وskip_name_resolve = ON وbind_address = 10.10.20.11.
    عرض تلميحprintf '%s\n' '[mysqld]' 'innodb_buffer_pool_size = 10G' … > /etc/mysql/mariadb.conf.d/90-production.cnf
    عرض الحل
    printf '%s\n' '[mysqld]' 'innodb_buffer_pool_size = 10G' 'innodb_log_file_size = 2G' 'innodb_flush_log_at_trx_commit = 1' 'max_connections = 300' 'skip_name_resolve = ON' 'bind_address = 10.10.20.11' > /etc/mysql/mariadb.conf.d/90-production.cnf

    التحقق : f=/etc/mysql/mariadb.conf.d/90-production.cnf; grep -qx '\[mysqld\]' $f && grep -Eq '^innodb[_-]buffer[_-]pool[_-]size *= *10G *$' $f && grep -Eq '^innodb[_-]log[_-]file[_-]size *= *2G *$' $f && grep -Eq '^innodb[_-]flush[_-]log[_-]at[_-]trx[_-]commit *= *1 *$' $f && grep -Eq '^max[_-]connections *= *300 *$' $f && grep -Eq '^skip[_-]name[_-]resolve *(= *(ON|1))? *$' $f && grep -Eq '^bind[_-]address *= *10\.10\.20\.11 *$' $f

  2. تحقّق بواسطة cnf-effectif innodb_buffer_pool_size (أو my_print_defaults --mysqld): قيمك ليست تلك التي سيعتمدها الخادم. ابحث عن الملف الذي يطغى عليها وعطّله دون حذفه (أعد تسميته حتى لا يُقرأ بعد الآن). يجب أن يكون الـ buffer pool الفعلي 10G وmax_connections مساويًا لـ 300.
    عرض تلميحداخل !includedir تُقرأ الملفات بالترتيب الأبجدي، ولا يُحتسب إلا ما ينتهي بـ .cnf: الملف 99-… يُقرأ بعد 90-…
    عرض الحل
    mv /etc/mysql/mariadb.conf.d/99-optimisation-2023.cnf /etc/mysql/mariadb.conf.d/99-optimisation-2023.cnf.desactive

    التحقق : test "$(cnf-effectif innodb_buffer_pool_size)" = 10G && test "$(cnf-effectif max_connections)" = 300 && ls /etc/mysql/mariadb.conf.d/ | grep '^99-optimisation-2023' | grep -vq '\.cnf$'

  3. يجب أن تتمكّن الخدمة من فتح عدد كبير من الملفات وأن يكون لديها الوقت لتفريغ buffer pool كبير عند الإيقاف: أنشئ ملف التجاوز (override) الخاص بـ systemd وهو /etc/systemd/system/mariadb.service.d/limites.conf مع LimitNOFILE=65535 وTimeoutStopSec=900 داخل [Service].
    عرض تلميحmkdir -p /etc/systemd/system/mariadb.service.d ثم printf '%s\n' '[Service]' 'LimitNOFILE=65535' 'TimeoutStopSec=900' > …/limites.conf (على خادم حقيقي: systemctl daemon-reload)
    عرض الحل
    mkdir -p /etc/systemd/system/mariadb.service.d && printf '%s\n' '[Service]' 'LimitNOFILE=65535' 'TimeoutStopSec=900' > /etc/systemd/system/mariadb.service.d/limites.conf

    التحقق : f=/etc/systemd/system/mariadb.service.d/limites.conf; grep -qx '\[Service\]' $f && grep -qx 'LimitNOFILE=65535' $f && grep -qx 'TimeoutStopSec=900' $f

  4. يجب ألا تنقل النواة ذاكرة الخادم إلى الـ swap إلا كملاذ أخير: اكتب /etc/sysctl.d/60-mariadb.conf مع vm.swappiness = 1.
    عرض تلميحecho 'vm.swappiness = 1' > /etc/sysctl.d/60-mariadb.conf (ثم sysctl --system على خادم حقيقي)
    عرض الحل
    echo 'vm.swappiness = 1' > /etc/sysctl.d/60-mariadb.conf

    التحقق : grep -Eq '^vm\.swappiness *= *1 *$' /etc/sysctl.d/60-mariadb.conf

  5. يجب أن تعيش البيانات على وحدة تخزين خاصة بها: أضف إلى /etc/fstab (دون لمس الأسطر الأخرى) تركيب الوحدة /dev/vg_bdd/lv_mysql على /var/lib/mysql، بنظام xfs، مع الخيار noatime، وفحص fsck بالقيمتين 0 2.
    عرض تلميحecho '/dev/vg_bdd/lv_mysql /var/lib/mysql xfs defaults,noatime 0 2' >> /etc/fstab (علامتا >> تعنيان الإضافة إلى نهاية الملف)
    عرض الحل
    echo '/dev/vg_bdd/lv_mysql /var/lib/mysql xfs defaults,noatime 0 2' >> /etc/fstab

    التحقق : awk '$1=="/dev/vg_bdd/lv_mysql" && $2=="/var/lib/mysql" && $3=="xfs" && $4 ~ /noatime/ && $6=="2" {ok=1} END {exit !ok}' /etc/fstab && grep -q '/boot' /etc/fstab && grep -q 'lv_swap' /etc/fstab