الرئيسيةمن أناالدوراتالمدونةسوق الأوامرالمناهج والباقاتالشركاء

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • المناهج والباقات
  • سوق الأوامر
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالمناهجالمدونةالدخول
البرمجة بالعربي

حوض الاتصالات: ليه فتح اتصال جديد لكل استعلام بيخنق قاعدة بياناتك

متوسط2 أغسطس 20264 دقائق قراءة
حوض الاتصالات: ليه فتح اتصال جديد لكل استعلام بيخنق قاعدة بياناتك
المستوى المطلوب: متوسط — المقال مناسب لمن يبني تطبيق ويب فيه قاعدة بيانات علائقية (PostgreSQL أو MySQL) ويريد يفهم إزاي يدير اتصالاتها بكفاءة. الافتراض إن عندك تطبيق ويب عادي بترافيك متوسط وبتستخدم قاعدة بيانات واحدة.

حوض الاتصالات (Connection Pool) وإدارة اتصالات قاعدة البيانات

لو استعلام بسيط بياخد وقت زيادة والسيرفر فاضي، المشكلة غالبًا مش في الاستعلام نفسه، دي في إن كل طلب بيفتح اتصال جديد لقاعدة البيانات من الصفر. المقال ده هيوريك إزاي تعيد استخدام الاتصالات فتوفّر عشرات المللي ثانية في كل طلب وتحمي قاعدة بياناتك من الانهيار تحت الضغط.

المشكلة باختصار

فتح اتصال جديد لـ PostgreSQL مش عملية رخيصة. بيحصل فيها مصافحة TCP، وغالبًا مصافحة TLS، وبعدين مصادقة (authentication) وتجهيز جلسة على السيرفر. ده بياخد في المعتاد من 5 لـ 30 مللي ثانية لكل اتصال، وممكن يوصل أكتر لو السيرفر بعيد جغرافيًا. زيّ فتح استعلامك نفسه اللي بياخد 2 مللي ثانية، لكن تأسيس الاتصال قبله بياخد 20. يعني 90% من الوقت راح في حاجة مالهاش علاقة بشغلك الفعلي.

ليه فتح اتصال جديد بيخنق السيرفر

تخيّل بنك بيوظّف موظّف صرف جديد لكل عميل يدخل، ويطرده بمجرد ما العميل يمشي. توظيف وتدريب الموظّف كل مرة بياخد وقت أطول من الخدمة نفسها، ولو دخل 100 عميل في نفس اللحظة هتحتاج 100 مكتب. ده بالظبط اللي بيحصل لما كل طلب يفتح اتصال جديد.

علميًا: PostgreSQL بيعمل لكل اتصال عملية (process) مستقلة على السيرفر، وكل واحدة بتستهلك ذاكرة تقديرية من 5 لـ 10 ميجابايت. وفيه حد أقصى اسمه max_connections قيمته الافتراضية 100. يعني لو ترافيكك زاد وكل طلب بيفتح اتصال، هتضرب السقف بسرعة وتبدأ ترفض طلبات بخطأ "too many connections"، حتى لو المعالج مش مشغول.

الحل: حوض اتصالات ثابت يُعاد استخدامه

بدل ما تفتح وتقفل كل مرة، افتح عدد محدود من الاتصالات مرة واحدة وسيبهم مفتوحين، وكل طلب "يستعير" اتصال جاهز، يستخدمه، ثم يرجّعه للحوض. خطوات الفكرة:

  1. عند تشغيل التطبيق، افتح عدد ثابت من الاتصالات (مثلًا 10) وخزّنهم في الحوض.
  2. كل طلب ياخد اتصال جاهز من الحوض بدل ما يعمل مصافحة جديدة.
  3. بعد الاستعلام، رجّع الاتصال للحوض بدل ما تقفله.
  4. لو كل الاتصالات مشغولة، الطلب ينتظر في طابور بدل ما يفتح اتصال إضافي على السيرفر.
Python
# مثال شغّال باستخدام psycopg2 pool على PostgreSQL
from psycopg2 import pool

# minconn=2, maxconn=10 -> نفتح الاتصالات مرة واحدة عند بدء التطبيق
db_pool = pool.SimpleConnectionPool(
    2, 10,
    host="localhost", dbname="app", user="app", password="secret"
)

def get_user(user_id):
    conn = db_pool.getconn()          # استعارة اتصال جاهز من الحوض
    try:
        with conn.cursor() as cur:
            cur.execute("SELECT email FROM users WHERE id = %s", (user_id,))
            return cur.fetchone()
    finally:
        db_pool.putconn(conn)         # رجّع الاتصال بدل ما تقفله

لو قِست الفرق: من غير حوض، ألف طلب متتابع كل واحد بيفتح اتصال جديد بـ 20 مللي ثانية تأسيس = 20 ثانية ضايعة في التأسيس وحده. مع الحوض، إعادة الاستخدام بتاخد أجزاء من المللي ثانية، فالـ 20 ثانية دي بتختفي تقريبًا بالكامل. المكسب الحقيقي بيظهر تحت الضغط: قاعدة بياناتك بتشتغل على 10 اتصالات ثابتة بدل ما تنفجر لمئات.

حجم الحوض: المقايضة اللي لازم تنتبه لها

الـ trade-off هنا في حجم الحوض. لو الحوض صغير أوي، الطلبات هتستنى في طابور فيزيد زمن الاستجابة. لو كبير أوي، بتفتح ضغط زيادة على قاعدة البيانات: كل اتصال بياخد ذاكرة، وكتر الاتصالات النشطة بيزوّد تبديل السياق (context switching) على السيرفر فيبطّئ الكل. يعني تكسب استجابة أسرع مقابل استهلاك ذاكرة أعلى، لحد نقطة بينقلب فيها المكسب لخسارة.

قاعدة تقريبية مشهورة من مشروع HikariCP لتحديد نقطة البداية: عدد الاتصالات = (عدد الأنوية × 2) + عدد أقراص التخزين. يعني سيرفر بـ 4 أنوية وقرص واحد يبدأ من حوالي 9 اتصالات، مش 100. الرقم ده أقل بكتير مما يتوقّع الناس، وده مقصود: اتصالات أقل نشطة غالبًا بتدّي throughput أعلى من عشرات الاتصالات المتزاحمة.

متى لا تستخدم هذه الطريقة

حوض الاتصالات داخل التطبيق مش الحل في كل الحالات:

  • في البيئات serverless (زي AWS Lambda)، كل نسخة قصيرة العمر وبتفتح حوضها الخاص، فبتتراكم مئات الاتصالات على قاعدة البيانات. هنا استخدم موزّع خارجي زي PgBouncer في وضع transaction pooling بدل الحوض الداخلي.
  • لو تطبيقك بسيط جدًا وترافيكه ضئيل (سكربت بيشتغل مرة في اليوم)، الحوض بيزوّد تعقيد بلا فايدة تُذكر.

الخطوة التالية

افتح إعدادات الاتصال في تطبيقك دلوقتي وشوف: هل بتفتح اتصال جديد جوه كل دالة request، ولا عندك حوض واحد بيتفتح مرة عند بدء التطبيق؟ لو الأولى، حوّلها لحوض بحجم يبدأ من (الأنوية × 2) + الأقراص، وراقب pg_stat_activity لتشوف عدد الاتصالات النشطة قبل وبعد.

المصادر

  • توثيق PostgreSQL الرسمي عن الاتصالات وmax_connections: postgresql.org/docs
  • PgBouncer — موزّع اتصالات خفيف لـ PostgreSQL: pgbouncer.org
  • HikariCP — About Pool Sizing (معادلة حجم الحوض): github.com/brettwooldridge/HikariCP
  • توثيق psycopg2 عن الـ connection pooling: psycopg.org/docs
  • SQLAlchemy — Connection Pooling: docs.sqlalchemy.org

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة