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

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

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

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

المنصة

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

الدعم

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

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

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

PgBouncer: ليه Postgres بيرفض الاتصالات تحت الضغط وإزاي تحلها في 10 دقايق

متوسط16 يوليو 20264 دقائق قراءة
PgBouncer: ليه Postgres بيرفض الاتصالات تحت الضغط وإزاي تحلها في 10 دقايق

هذا المقال يتطلب مستوى متوسط. لو تعاملت قبل كده مع قاعدة بيانات وربطتها بتطبيق ويب، هتكون قادر تنفّذ كل خطوة هنا.

لو تطبيقك بيرمي FATAL: sorry, too many clients already وقت الذروة والـ CPU والرام مرتاحين، المشكلة مش في السيرفر. المشكلة إن كل اتصال بيفتح عملية كاملة في Postgres، والعمليات دي خلصت. الحل هو مجمّع اتصالات اسمه PgBouncer، وهتقدر تركّبه وتضبطه في أقل من 10 دقايق.

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

Postgres بيتعامل مع كل اتصال بموديل عملية-لكل-اتصال (process per connection). يعني كل client بيفتح اتصال، السيرفر بيعمل له عملية نظام تشغيل مستقلة، بتاخد ذاكرة ثابتة حتى وهي قاعدة مش بتعمل حاجة. لما تطبيقك يتوسّع ويفتح مئات الاتصالات المتزامنة، بتوصل لسقف max_connections بسرعة، وأي اتصال جديد بيترفض.

خلّينا نفهمها بمثال بسيط الأول

تخيّل مطعم فيه 20 طاولة بس. لو كل زبون دخل احتجز طاولة كاملة وقعد عليها طول الوقت — حتى وهو بيتكلم في التليفون ومش بياكل — الطاولات هتخلص بسرعة، وأي زبون جديد هيتقفل في وشه الباب. لكن المطعم الذكي بيخلّي في مضيف على الباب: الزباين يستنوا شوية، وأول ما طاولة تفضى، حد ياخدها، ياكل بسرعة، ويقوم. نفس الـ 20 طاولة بتخدم 600 زبون في الساعة.

PgBouncer هو المضيف ده. بدل ما كل طلب من تطبيقك يحجز "طاولة" (اتصال Postgres) لنفسه ويقعد عليها، PgBouncer بيمسك عدد صغير من الاتصالات الحقيقية ويوزّعها على مئات الطلبات. الطلب بياخد اتصال، ينفّذ الـ query، ويرجّعه فورًا لغيره.

الشرح العلمي الدقيق

PgBouncer هو connection pooler خفيف بيقعد بين تطبيقك وبين Postgres. بيدعم ثلاث أنماط تجميع: session، transaction، statement. الأكثر فائدة للتطبيقات الويب هو transaction pooling: الاتصال الحقيقي بيرجع للـ pool بمجرد انتهاء كل transaction، مش بعد ما الـ client يقفل. النتيجة إن 600 اتصال من التطبيق ممكن يتخدموا بـ 20 اتصال حقيقي بس على قاعدة البيانات.

الافتراض هنا إن طلباتك قصيرة (أقل من 100 مللي ثانية للـ query الواحد) وما بتفضلش ماسكة الاتصال طويلًا. ده الحال الشائع في REST APIs وتطبيقات الويب.

الحل خطوة بخطوة

  1. ركّب PgBouncer على نفس سيرفر التطبيق أو سيرفر مستقل قريب من الداتابيز.
  2. اكتب ملف الإعداد pgbouncer.ini.
  3. حدّد مستخدمي الاتصال في userlist.txt.
  4. وجّه تطبيقك على منفذ PgBouncer (6432) بدل منفذ Postgres (5432).

التركيب على Ubuntu/Debian:

Bash
# التركيب
sudo apt-get update && sudo apt-get install -y pgbouncer

# تشغيل الخدمة
sudo systemctl enable --now pgbouncer

ملف /etc/pgbouncer/pgbouncer.ini:

[databases]
myapp = host=127.0.0.1 port=5432 dbname=myapp

[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt

# النمط الأهم: يرجّع الاتصال بعد كل معاملة
pool_mode = transaction

# 20 اتصال حقيقي فقط لكل قاعدة
default_pool_size = 20

# سقف اتصالات العملاء الواردة
max_client_conn = 1000

بعد ما تشغّل، غيّر سلسلة الاتصال في تطبيقك من:

Bash
# قبل — على Postgres مباشرة
postgresql://myapp:pass@127.0.0.1:5432/myapp

# بعد — على PgBouncer
postgresql://myapp:pass@127.0.0.1:6432/myapp

الأرقام: قبل وبعد

لو عندك API بيخدم 50 ألف زائر يوميًا، وكل instance بيفتح 100 اتصال، وعندك 6 instances، ده 600 اتصال على Postgres. بفرض إن الـ backend الواحد بياخد حوالي 9 ميجابايت، ده يعني ~5.4 جيجابايت رام تتاكل في الاتصالات وحدها، وده كفيل يخنق السيرفر. بعد transaction pooling، نفس الحمل بيتخدم بـ 20 اتصال حقيقي فقط — أقل من 200 ميجابايت.

راقب الفرق بأمر داخل Postgres:

SQL
SELECT count(*) FROM pg_stat_activity;

الـ trade-off هنا

transaction pooling بيوفّر الذاكرة، بس بتخسر مميزات مربوطة بالجلسة (session). الأشياء دي بتتكسر أو بتحتاج ضبط: prepared statements على مستوى الجلسة، LISTEN/NOTIFY، advisory locks، والمتغيّرات المؤقتة زي SET. لو تطبيقك بيعتمد على أي منها، لازم تعيد ضبطها أو تستخدم session pooling بدل transaction.

الخلاصة: بتكسب تقليل هائل في الذاكرة والاتصالات، بتخسر بعض ميزات الجلسة. لو بتبني REST API عادي، ده تبادل رابح جدًا.

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

متستخدمش transaction pooling لو تطبيقك بيمسك اتصال طويل عمدًا — زي worker بيعمل معاملة كبيرة ممتدة، أو نظام بيعتمد على LISTEN/NOTIFY بشكل أساسي. وكمان لو عدد اتصالاتك المتزامنة أصلًا أقل من max_connections بكتير (مثلًا تطبيق صغير بـ 20 اتصال فقط)، PgBouncer هيضيف طبقة تعقيد بدون فايدة حقيقية. الأداة دي بتلمع لما يكون عندك مئات الاتصالات القصيرة.

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

افتح Postgres دلوقتي ونفّذ SELECT count(*) FROM pg_stat_activity; وقت الذروة. لو الرقم قريب من max_connections، ركّب PgBouncer بإعداد pool_mode = transaction و default_pool_size = 20، ووجّه تطبيقك على المنفذ 6432. قِس نفس العدّاد بعد ساعة — لو نزل لعشرات بدل مئات، الحل شغّال.

المصادر

  • توثيق PgBouncer الرسمي — أنماط التجميع وملف الإعداد: pgbouncer.org/config.html
  • توثيق PostgreSQL — إدارة الاتصالات وmax_connections: postgresql.org/docs
  • PostgreSQL Wiki — تقدير تكلفة الذاكرة لكل اتصال: wiki.postgresql.org
  • توثيق pg_stat_activity: postgresql.org/docs/monitoring

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

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

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