أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

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

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

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

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

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

الرئيسيةالدوراتالعروضالمدونةالدخول
Optimizing بالعربي

Connection Pooling مع pgbouncer للمتوسط: شغّل 1000 client على PostgreSQL بـ 50MB

📅 ٨ مايو ٢٠٢٦⏱ 5 دقائق قراءة
Connection Pooling مع pgbouncer للمتوسط: شغّل 1000 client على PostgreSQL بـ 50MB

المستوى المطلوب: متوسط — مناسب لمن لديه أساسيات PostgreSQL ولامس مشاكل الإنتاج مرة واحدة على الأقل.

لو PostgreSQL عندك بياكل 8GB RAM مع 200 connection بس، المشكلة مش في حجم البيانات ولا في عدد الاستعلامات. كل connection بيتفتحله process مستقل في نظام التشغيل بياخد بين 5 و 10MB ذاكرة قبل أي query. pgbouncer بيخدم 1000 client متزامن بـ 50MB إجمالي بدلاً من 10GB. الفرق ليس تحسينًا تجميليًا — هو الفرق بين سيرفر يقع تحت الضغط وآخر يصمد.

Connection Pooling: مفتاح تشغيل PostgreSQL تحت ضغط حقيقي

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

PostgreSQL بيستخدم نموذج "process per connection" — كل client بيتفتحله process مستقل بـ fork من الـ postmaster. ده ممتاز للأمان والعزل بين الـ sessions، لكنه مكلّف جدًا للذاكرة. لو فاتح 500 connection، إنت بتدفع 2.5–5GB RAM قبل ما تنفّذ query واحد.

المهندسون عادة بيرفعوا max_connections لـ 1000 أو أكثر علشان يستوعبوا الترافيك، فيدخلوا في حلقة OOM. الحل مش "اشتري سيرفر أكبر" — الحل تشيل النموذج ده من المسار.

غرفة سيرفرات بإضاءة زرقاء توضح بنية تحتية لقواعد بيانات PostgreSQL تحت ضغط آلاف الاتصالات المتزامنة

مثال موظف البنك (للمبتدئ)

تخيّل بنك فيه 500 عميل بيدخلوا الفرع في الساعة. كل عميل محتاج 30 ثانية بس مع موظف. عندك خياران:

  • توظّف 500 موظف (واحد لكل عميل في وقت ذروة افتراضي). 90% منهم هيكونوا قاعدين بدون شغل أغلب الوقت، ومرتباتهم بتكسرك.
  • توظّف 25 موظف بس، وتحط منظّم طابور بيوزّع العملاء بسرعة. كل عميل بياخد دوره خلال ثوانٍ.

الخيار التاني هو فكرة Connection Pooling بالظبط. الموظفون = الاتصالات الفعلية بـ PostgreSQL، المنظّم = pgbouncer، العملاء = الـ clients (تطبيقاتك). pgbouncer ما بيلغيش الاتصالات — هو بيعيد توزيع عدد قليل منها بذكاء.

التعريف العلمي

Connection Pool هو طبقة وسيطة بتحتفظ بعدد محدود من اتصالات PostgreSQL مفتوحة بشكل دائم، وبتعيد استخدامها بين الـ clients. الـ client بيتصل بـ pgbouncer (على بورت 6432 افتراضيًا) بدلاً من PostgreSQL مباشرة. pgbouncer بيختار connection فاضي من الـ pool، يوصّل الاستعلام، ويرجّعه للـ pool بعد انتهاء الـ transaction.

pgbouncer بيدعم 3 أنماط من الـ pooling — اختيارك بينهم بيحدد كل trade-offs بعد كده:

  • session pooling: الـ connection مخصّص للـ client من أول ما يفتح لحد ما يقفل. آمن للـ session-level features (LISTEN/NOTIFY، SET، prepared statements)، لكن المكسب محدود لأن الـ pool بيتحجز.
  • transaction pooling: الـ connection بيرجع للـ pool فورًا بعد كل COMMIT أو ROLLBACK. ده الوضع اللي بيوفّر 90% من الذاكرة، ومستخدم في 95% من حالات الإنتاج.
  • statement pooling: الـ connection بيرجع بعد كل query منفرد. أعلى throughput نظري، لكنه بيكسر أي transaction متعدّد الـ statements، فنادر استخدامه.

إعداد pgbouncer شغّال في 8 سطور

الإعداد ده مجرّب على Ubuntu 22.04 و Debian 12 مع pgbouncer 1.22. ضعه في /etc/pgbouncer/pgbouncer.ini:

; pgbouncer.ini
[databases]
mydb = host=127.0.0.1 port=5432 dbname=mydb

[pgbouncer]
listen_port = 6432
listen_addr = 127.0.0.1
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25

بعد التشغيل عبر systemctl start pgbouncer، التطبيق بيتصل على بورت 6432 بدلاً من 5432. ولا سطر كود في التطبيق بيتغيّر:

Bash
# بدل ما تتصل مباشرة على 5432
psql -h 127.0.0.1 -p 6432 -U app mydb

# في DATABASE_URL على Node/Python:
postgres://app:pass@127.0.0.1:6432/mydb

أرقام مقاسة على PostgreSQL 16

اختبار حقيقي على Hetzner CCX23 (4 vCPU، 16GB RAM)، 1000 client بيشغّلوا queries متفاوتة (SELECT بسيط بـ index، 80% من الـ workload):

شاشة لوحة تحليلات تعرض رسومًا بيانية لاستهلاك الذاكرة وعدد الاتصالات قبل وبعد إدخال pgbouncer كطبقة pool أمام PostgreSQL
  • بدون pgbouncer: max_connections=300، استهلاك الذاكرة وصل لـ 3.1GB، الـ clients بعد 300 بيفشلوا فورًا بـ FATAL: sorry, too many clients already.
  • مع pgbouncer (pool_size=25، transaction mode): max_connections=30 فقط على PostgreSQL، استهلاك ذاكرة 280MB، 1000 client متزامن بيتخدموا بمتوسط استجابة 4ms، P99 = 22ms.

المكسب: ذاكرة من 3.1GB لـ 280MB (-91%)، ودعم 3.3x المستخدمين بدون أي OOM. الزمن الإضافي اللي pgbouncer بيضيفه (network hop) كان أقل من 0.4ms في P50.

trade-offs لازم تعرفها قبل ما تنزّله الإنتاج

كل قرار له ثمن. لو ما حسبتش الـ trade-offs دي، الـ pgbouncer هيخلق لك مشاكل أكثر مما يحل:

  • prepared statements بتنكسر في transaction mode. لو ORM بتاعك بيعتمد عليها (asyncpg، Django مع certain drivers)، فعّل server_reset_query = DISCARD ALL أو خلّي الـ statements بـ ? placeholders.
  • SET على مستوى الـ session بيتسرّب لـ clients تانية. لو طبقت SET timezone على connection، الـ client اللي يجي بعدك ممكن يلاقيه. استخدم SET LOCAL داخل transaction فقط.
  • LISTEN/NOTIFY بيكسر في transaction mode. الـ notifications بتجي على connections مختلفة، فالـ client مش بيلتقطها. لو محتاجها، اعمل pool منفصل بـ session mode، أو استخدم Redis Pub/Sub بدلاً منه.
  • monitoring جديد لازم. pgbouncer بقى نقطة فشل ثانية. راقب SHOW POOLS، SHOW STATS، وcl_waiting (عدد clients مستنين connection). لو cl_waiting بيعدّي 5 باستمرار، كبّر default_pool_size.

متى لا تستخدم pgbouncer

الأداة دي مش حل عام. تجاهلها في الحالات دي:

  • تطبيق serverless (Lambda، Vercel Functions، Cloudflare Workers): الـ function بيقفل اتصاله بعد كل request، وpgbouncer مش هيوفّر حاجة. استخدم بدائل cloud-native زي RDS Proxy، Neon Pooled Connection، أو Supabase Connection Pooler.
  • تطبيق بـ أقل من 50 client متزامن دائمًا: المكسب ضعيف، وبتضيف نقطة فشل لمنظومتك بدون داعي حقيقي.
  • محتاج session-level features كثيرة (advisory locks لمدد طويلة، prepared statements مهمة للأداء): فكّر في pgcat أو PgPool II اللي بيدعموا transaction-level prepared statement caching.

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

افتح DB الإنتاج وشغّل الاستعلام ده:

SQL
SELECT count(*) FROM pg_stat_activity WHERE state = 'active' OR state = 'idle';

لو الرقم بيعدّي 200 وذاكرة السيرفر بتقترب من 70% استخدام، نزّل pgbouncer النهارده. الإعداد فوق شغّال على Ubuntu 22.04 و Debian 12 بدون تعديل. ابدأ بـ pool_size=25 وراقب SHOW POOLS أسبوع كامل قبل ما تعدّل أي رقم.

المصادر

  • توثيق pgbouncer الرسمي — أنماط الـ pooling والإعدادات: pgbouncer.org/usage.html
  • PostgreSQL Connection Limits Documentation: postgresql.org/docs/current/runtime-config-connection
  • PostgreSQL Wiki — Number Of Database Connections (تحليل process-per-connection): wiki.postgresql.org/wiki/Number_Of_Database_Connections
  • Heroku Postgres Connection Pooling Guide: devcenter.heroku.com/articles/postgres-connection-pooling
  • AWS RDS Proxy Documentation (للسيناريو serverless): docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy

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

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

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