المهندس في مناوبة الطوارئ الذي لا يستطيع الإجابة على "أي استعلام يقتلنا الآن؟" في أقل من 60 ثانية، يطير بلا مؤشرات. مراقبة قواعد البيانات ليست مجرد لوحات معلومات — إنها مجموعة منضبطة من الإشارات وخطوط جمع البيانات وقواعد التنبيه التي تتيح لك الانتقال من "شيء ما خطأ" إلى "هذا الاستعلام تحديدًا، يحمل هذا القفل، على هذه النسخة المتماثلة، بهذا التأخّر" في دقائق لا ساعات. يغطي هذا الدرس الركائز الأربع الأكثر أهمية في الإنتاج: قياس الاستعلامات البطيئة، وتحليل الأقفال، وتتبع تأخّر النسخ المتماثلة، وتجميع إحصائيات الاستعلامات.
قياس الاستعلامات البطيئة
لكل محرك قواعد بيانات في الإنتاج آلية مدمجة لتسجيل الاستعلامات التي تتجاوز عتبة زمنية قابلة للضبط. تفعيل هذه الآلية بشكل صحيح هو أعلى استثمار في المراقبة من حيث العائد.
PostgreSQL — log_min_duration_statement
اضبط log_min_duration_statement = 1000 (بالمللي ثانية) في postgresql.conf أو عبر ALTER SYSTEM لتسجيل أي عبارة تستغرق أكثر من ثانية واحدة. في البيئات عالية التدفق، ابدأ بـ 500 مللي ثانية وخفّض القيمة تدريجيًا مع اكتساب الثقة. يتضمن سطر السجل المدة ونص الاستعلام والمعاملات المرتبطة (إذا جُمع مع log_parameters)، ويمكن التقاط خطة الاستعلام عبر إضافة auto_explain.
-- تفعيل auto_explain للاستعلامات التي تتجاوز 500 مللي ثانية (PostgreSQL)
-- أضف إلى postgresql.conf أو شغّل بصلاحية superuser:
ALTER SYSTEM SET shared_preload_libraries = 'auto_explain';
ALTER SYSTEM SET auto_explain.log_min_duration = 500;
ALTER SYSTEM SET auto_explain.log_analyze = on;
ALTER SYSTEM SET auto_explain.log_buffers = on;
ALTER SYSTEM SET auto_explain.log_format = 'json';
ALTER SYSTEM SET log_min_duration_statement = 500;
SELECT pg_reload_conf();
-- عرض أبطأ 20 استعلامًا موحّدًا (يتطلب pg_stat_statements)
SELECT
calls,
round(total_exec_time::numeric, 2) AS total_ms,
round((total_exec_time / calls)::numeric, 2) AS avg_ms,
round(stddev_exec_time::numeric, 2) AS stddev_ms,
rows,
shared_blks_hit,
shared_blks_read,
left(query, 120) AS query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;
يكشف عمود shared_blks_read عن ضغط فقدان ذاكرة التخزين المؤقت — الاستعلام ذو القراءات المرتفعة نسبةً إلى حالات الإصابة يسحب البيانات من القرص، وهو علامة تحذير بصرف النظر عن وقت تنفيذه الفعلي. أعد ضبط الإحصائيات بين عمليات النشر بـ SELECT pg_stat_statements_reset(); للحصول على خط أساس نظيف.
MySQL / MariaDB — سجل الاستعلامات البطيئة
اضبط slow_query_log = ON وlong_query_time = 1 وlog_queries_not_using_indexes = ON. حلّل التقارير المجمّعة بـ pt-query-digest (Percona Toolkit) — فهو يوحّد بصمات الاستعلامات، ويحسب النسب المئوية، ويُنتج مخرجات منظمة تتصل مباشرةً بمكدس مراقبتك.
# تحليل سجل الاستعلامات البطيئة في MySQL باستخدام pt-query-digest
pt-query-digest \
--since "$(date -d '1 hour ago' '+%Y-%m-%d %H:%M:%S')" \
--order-by Query_time:sum \
--limit 20 \
/var/lib/mysql/slow.log
# إرسال سجلات الاستعلامات البطيئة إلى Loki للاحتفاظ طويل الأمد
# مقتطف من /etc/promtail/config.yaml
scrape_configs:
- job_name: mysql-slow
static_configs:
- targets: [localhost]
labels:
job: mysql-slow
host: db-primary-01
pipeline_stages:
- regex:
expression: '# Time: (?P<ts>\S+)\n# User.*\n# Query_time: (?P<query_time>\S+).*\n(?P<query>.*)'
- labels:
query_time:
__path__: /var/lib/mysql/slow.log
النسبة المئوية p99 لا المتوسط: المتوسط يخفي الذيل. ابنِ لوحات المعلومات دائمًا حول p99 (وأحيانًا p999 للأنظمة المالية أو الحرجة). الاستعلام الذي يبلغ متوسطه 5 مللي ثانية لكنه يقفز إلى 4,000 مللي ثانية عند p99 هو حادثة إنتاج في انتظار وقوعها. في Prometheus، احسب النسب المئوية من مخططات pg_stat_statements التي يعرضها postgres_exporter.
تحليل الأقفال
الأقفال هي القاتل الصامت في قواعد البيانات متعددة الكتّاب. إجراءٌ يحتفظ بقفل صف بينما ينتظر مدخلات المستخدم، أو قفل على مستوى جدول بسبب ترحيل مخطط عرضي، يمكن أن يُكوّم مئات الاتصالات المنتظرة في ثوانٍ.
إجراء حاجب واحد يحمل قفل صف يُكوّم اتصالات منتظرة متعددة — حل القفل الجذري (PID 42) يفتح السلسلة بأكملها.
-- PostgreSQL: تحديد الحجب وطوابير الانتظار
SELECT
blocker.pid AS blocker_pid,
blocker.usename AS blocker_user,
blocker.query AS blocker_query,
now() - blocker.xact_start AS blocker_age,
waiter.pid AS waiter_pid,
waiter.query AS waiter_query,
now() - waiter.state_change AS waiting_for
FROM pg_stat_activity waiter
JOIN pg_stat_activity blocker
ON blocker.pid = ANY(pg_blocking_pids(waiter.pid))
WHERE waiter.wait_event_type = 'Lock'
ORDER BY blocker_age DESC;
-- MySQL: عرض الإجراءات الحاجبة
SELECT
r.trx_id AS waiting_trx,
r.trx_mysql_thread_id AS waiting_thread,
r.trx_query AS waiting_query,
b.trx_id AS blocking_trx,
b.trx_mysql_thread_id AS blocking_thread,
b.trx_query AS blocking_query
FROM information_schema.innodb_lock_waits w
JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;
مهلة انتظار القفل ليست شبكة الأمان: القيمة الافتراضية لـ lock_timeout في PostgreSQL هي 0 (انتظار لا نهائي). في MySQL القيمة الافتراضية لـ innodb_lock_wait_timeout هي 50 ثانية. كلتا القيمتين خطيرتان في الأنظمة عالية الإنتاجية. اضبط lock_timeout = 5s في PostgreSQL على مستوى التطبيق، وراقب تجمّع الاتصالات لاكتشاف أي ارتفاع في أخطاء انتظار الأقفال — فهي مؤشر مبكر على مشكلة تنافس أعمق.
مقاييس تأخّر النسخ المتماثلة
تأخّر النسخ المتماثلة هو الثواني (أو البايتات) التي تتخلف بها النسخة عن النسخة الأساسية. إنه إشارة SLO حرجة: القراءة من نسخة متأخرة تُعيد بيانات قديمة، والنسخة التي لا تستطيع مواكبة العبء لن تستطيع اللحاق أبدًا في ظل حمل كتابة مستمر. تُنبّه الفرق في الشركات الكبرى على تأخّر التزامن بعتبتين — تحذير عند 10 ثوانٍ وتنبيه عاجل عند 60 ثانية — وتعامل أي اتجاه تصاعدي فوق الصفر كخلل في الأداء.
تأخّر النسخ في PostgreSQL يُقاس بالبايتات (فرق LSN) والثواني. المصدر الموثوق هو pg_stat_replication على النسخة الأساسية:
-- PostgreSQL: التأخّر بالبايتات والثواني لكل نسخة
SELECT
application_name,
client_addr,
state,
sync_state,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn)) AS send_lag,
pg_size_pretty(pg_wal_lsn_diff(sent_lsn, flush_lsn)) AS flush_lag,
pg_size_pretty(pg_wal_lsn_diff(flush_lsn, replay_lsn)) AS replay_lag,
write_lag, flush_lag AS flush_lag_time, replay_lag AS replay_lag_time
FROM pg_stat_replication
ORDER BY replay_lag DESC;
-- على النسخة نفسها: ثواني التأخّر
SELECT
now() - pg_last_xact_replay_timestamp() AS replication_lag_seconds;
-- قاعدة تنبيه Prometheus (PrometheusRule) لتأخّر النسخ
# prometheus-rules/database.yaml
groups:
- name: database.replication
rules:
- alert: PostgresReplicationLagWarning
expr: pg_replication_lag{job="postgres"} > 10
for: 2m
labels:
severity: warning
annotations:
summary: "Replica {{ $labels.instance }} is {{ $value | humanizeDuration }} behind primary"
- alert: PostgresReplicationLagCritical
expr: pg_replication_lag{job="postgres"} > 60
for: 1m
labels:
severity: critical
annotations:
summary: "CRITICAL: Replica lag {{ $value }}s — reads may return stale data"
- alert: MySQLSlaveIONotRunning
expr: mysql_slave_status_slave_io_running{job="mysql"} == 0
for: 30s
labels:
severity: critical
annotations:
summary: "MySQL replica IO thread stopped on {{ $labels.instance }}"
في MySQL، مقياس التأخّر المرجعي هو Seconds_Behind_Master من SHOW SLAVE STATUS — يعرضه mysqld_exporter كـ mysql_slave_status_seconds_behind_master. تعامل مع هذه القيمة بحذر: تُعاد إلى 0 حين يتوقف خيط IO، مما يُعطي إيجابية كاذبة. راقب دائمًا قيمة التأخّر وحالة تشغيل خيطَي IO/SQL معًا.
تجميع إحصائيات الاستعلامات عبر pg_stat_statements وPerformance Schema
تحليل الاستعلامات البطيئة المخصّص يلتقط الحوادث الفردية. أما تجميع إحصائيات الاستعلامات فيكتشف الأنماط المنهجية — الاستعلام السريع بمفرده لكنه يمثّل 40% من إجمالي وحدة المعالجة لقاعدة البيانات لأنه يُنفَّذ 50,000 مرة في الدقيقة. هذا هو الفرق بين إخماد الحرائق وتخطيط السعة.
تُوحّد pg_stat_statements في PostgreSQL بصمات الاستعلامات وتجمع عدّادات لكل استعلام طوال عمر الخادم. تخدم events_statements_summary_by_digest في Performance Schema بـ MySQL الغرض ذاته. يجب الاستعلام عن كليهما بجدول زمني (كل 1-5 دقائق) وإرسال الفروق إلى نظام المقاييس الخاص بك.
-- PromQL: تحديد الاستعلامات المهيمنة على وقت تنفيذ قاعدة البيانات
-- (postgres_exporter يعرض pg_stat_statements كـ Gauge لكل بصمة استعلام)
# معدل وقت التنفيذ الإجمالي لكل استعلام (أعلى 5 من حيث استهلاك المعالج)
topk(5,
rate(pg_stat_statements_total_exec_time_seconds_total[5m])
)
# نسبة إصابة ذاكرة التخزين المؤقت لكل استعلام — أقل من 0.95 يعني قراءات من القرص
1 - (
rate(pg_stat_statements_blks_read_total[5m])
/
(rate(pg_stat_statements_blks_hit_total[5m]) + rate(pg_stat_statements_blks_read_total[5m]) + 0.001)
)
-- MySQL: أعلى 10 ملخصات حسب إجمالي وقت الاستجابة (Performance Schema)
SELECT
schema_name,
LEFT(digest_text, 100) AS query_fingerprint,
count_star AS executions,
round(sum_timer_wait / 1e12, 2) AS total_latency_s,
round(avg_timer_wait / 1e9, 2) AS avg_latency_ms,
sum_rows_examined,
sum_rows_sent
FROM performance_schema.events_statements_summary_by_digest
WHERE schema_name IS NOT NULL
ORDER BY sum_timer_wait DESC
LIMIT 10;
مكدس المراقبة لقواعد البيانات على نطاق واسع: Prometheus مع postgres_exporter (أو mysqld_exporter) يجمع مقاييس المحرك كل 15 ثانية. لوحات Grafana تعرض الإشارات الأربع الذهبية المُكيّفة لقواعد البيانات: معدل الأخطاء (فشل الاتصالات، والتوقفات التعارضية)، والاستجابة (وقت الاستعلام عند p99)، والحركة (استعلامات/ثانية)، والإشباع (ملء تجمّع الاتصالات، وتأخّر النسخ). Loki يستوعب سطور سجل الاستعلامات البطيئة للبحث النصي. PagerDuty يُوجّه التنبيهات الحرجة. هذا المكدس الرباعي — جمع، تخزين، تمثيل، تنبيه — هو الخط الأساسي في كل شركة سحابية كبرى.
بناء خط أساسي للمراقبة في 30 دقيقة
إذا كنت تبدأ من الصفر على مجموعة جديدة، فهذا هو ترتيب العمليات الذي يحقق 80% من قيمة الإنتاج بأسرع وقت:
فعّل pg_stat_statements (PostgreSQL) أو ملخصات Performance Schema (MySQL) — هذه آمنة للإنتاج وعادةً مُفعّلة بالفعل في الخدمات المُدارة.
انشر postgres_exporter أو mysqld_exporter كحاوية جانبية أو خدمة systemd؛ ووجّه Prometheus إليه بفترة جمع 15 ثانية.
استورد لوحات Grafana المجتمعية (ID 9628 لـ PostgreSQL، ID 7362 لـ MySQL) كنقطة بداية، ثم خصّص العتبات وفق نمط حركتك.
اضبط log_min_duration_statement = 500 (Postgres) أو فعّل سجل الاستعلامات البطيئة (MySQL)؛ وأرسل السجلات إلى Loki أو CloudWatch Logs Insights.
اكتب ثلاث قواعد تنبيه: تأخّر النسخ الحرج، وإشباع الاتصالات فوق 80%، ومعدل الاستعلامات البطيئة فوق خط الأساس.
الكشف التلقائي عن تراجع أداء الاستعلامات: على مستوى Google وMeta، يكون الكشف عن الاستعلامات البطيئة تلقائيًا — خطوة في CI تُشغّل EXPLAIN ANALYZE على كل استعلام جديد في طلب السحب وتُفشل البناء إذا تجاوزت تكلفة الخطة عتبة معينة. لمعظم الفرق، نسخة أبسط تُجدي: أرسل فروق pg_stat_statements إلى Datadog أو Grafana Cloud، واضبط تنبيهات اكتشاف الشذوذ على وقت تنفيذ كل بصمة. المرة الأولى التي تكتشف فيها إزالة فهرس سيئ في مراجعة الكود بدلًا من تنبيه الساعة الثالثة فجرًا، ستُدرك لماذا يستحق هذا الاستثمار عناءه.
مراقبة قواعد البيانات ليست إعدادًا يُنجز مرة واحدة — إنها حلقة تغذية راجعة. كل حادثة يجب أن تنتهي بقاعدة تنبيه جديدة أو لوحة معلومات جديدة حتى يُكتشف نمط الفشل ذاته تلقائيًا في المرة القادمة. الفرق التي تعامل المراقبة كنظام حيّ تكتشف المشكلات في دقائق؛ والفرق التي تعاملها كمتطلب إجراءات يكتشفونها في ساعات — أو لا يكتشفونها أبدًا.
نستخدم ملفات تعريف الارتباط لتشغيل هذا الموقع وتحليل الزيارات وعرض إعلانات مخصّصة. يمكنك قبول كل ملفات تعريف الارتباط أو رفض غير الأساسية منها.
سياسة الخصوصية