سؤال

أنا أستخدم Django مع Apache و MOD_WSGI و postgresql (كل شيء على نفس المضيف)، وأحتاج إلى التعامل مع الكثير من طلبات الصفحات الديناميكية البسيطة (مئات في الثانية). واجهت مشكلة أن الاختناق هو أن django لا تملك اتصال قاعدة بيانات مستمرة وإعادة توصيله في كل طلبات (يستغرق حوالي 5ms). أثناء القيام بمعيار، حصلت على ذلك مع اتصال مستمر يمكنني التعامل معنا بالقرب من 500 R / S بينما أحصل على 50 R / S فقط.

أي شخص لديه أي نصيحة؟ كيفية تعديل Django لاستخدام الاتصال المستمر؟ أو تسريع اتصال من بيثون إلى ديسيبل

شكرا لك مقدما.

هل كانت مفيدة؟

المحلول

جانغو 1.6 وأضافت دعم الاتصالات المستمرة (رابط Doc for django 1.9):

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

نصائح أخرى

محاولة PGBouncer. - مستحضر اتصال خفيف الوزن لل postgresql. سمات:

  • عدة مستويات من الوحشية عند الدوران الدوارة:
    • تجمع الجلسة
    • تجمع المعاملات
    • تجميع البيان
  • انخفاض متطلبات الذاكرة (2K لكل اتصال افتراضيا).

في جذع django، تحرير django/db/__init__.py والتعليق على الخط:

signals.request_finished.connect(close_connection)

يؤدي هذا المعالج الإشارة إلى قطع الاتصال قاعدة البيانات بعد كل طلب. أنا لا أعرف ما سيكون كل الآثار الجانبية للقيام بذلك، لكنه لا معنى له لبدء اتصال جديد بعد كل طلب؛ يدمر الأداء، كما لاحظت.

أنا أستخدم هذا الآن، لكنني لم أفعل مجموعة كاملة من الاختبارات لمعرفة ما إذا كان أي شيء يكسر.

لا أعرف لماذا يعتقد الجميع أن هذا يحتاج إلى خلفية جديدة أو مستحضر اتصال خاص أو حلول معقدة أخرى. يبدو أن هذا بسيط للغاية، على الرغم من أنني لا أشك في أن هناك بعض gotchas غامضة جعلتهم يفعلون ذلك في المقام الأول - والتي ينبغي التعامل معها أكثر معقولية؛ 5MS النفقات العامة لكل طلب هو الكثير جدا لخدمة عالية الأداء، كما لاحظت. (يستغرق مني 150ms- أنا هف لم أحسب لماذا حتى الآن.)

تحرير: تغيير ضروري آخر في django / الوسيطة / المعاملات. قم بإزالة الاختبارات الخاصة بالمعالجين. خلاف ذلك، لن يرتكب معاملة إذا قرأت فقط من قاعدة البيانات، والتي ستترك الأقفال مفتوحة يجب إغلاقها.

أنا خلقت صغيرة django التصحيح التي تنفذ تجمع اتصال من MySQL و postgresql عبر تجمع SQLAlchemy.

هذا يعمل تماما في إنتاج http://grandcapital.net/ لفترة طويلة من الزمن.

تمت كتابة التصحيح بعد googling الموضوع قليلا.

إخلاء المسئولية: أنا لم أحاول ذلك.

أعتقد أنك بحاجة إلى تنفيذ نهاية ظهر قاعدة بيانات مخصصة. هناك عدد قليل من الأمثلة على الويب الذي يوضح كيفية تنفيذ نهاية قاعدة بيانات الظهر مع تجمع الاتصالات.

من المحتمل أن يكون استخدام تجمع اتصال حلا جيدا بالنسبة لك الحال، حيث يتم فتح اتصالات الشبكة مفتوحة عند إرجاع الاتصالات إلى حمام السباحة.

  • هذا المشنور ينجز هذا عن طريق الترقيع Django (يشير أحد التعليقات إلى أنه من الأفضل تنفيذ نهاية ظهر مخصص خارج كود Django الأساسي)
  • هذا المشنور هو تنفيذ نهاية عرف DB

تستخدم كلا المشاريعين MySQL - ربما تكون قادرا على استخدام تقنيات مماثلة مع postgresql.

تعديل:

  • يذكر كتاب Django تجمع اتصال PostgresQL، باستخدام pgpool. (درس تعليمي).
  • شخص ما نشر التصحيح بالنسبة إلى PSYCOPG2 الخلفية التي تنفذ تجمع الاتصالات. أقترح إنشاء نسخة من نهاية الظهر الموجودة في مشروعك الخاص والتصحيح هذا واحد.

لقد قمت بإجراء بعض PSYCOPG2 مخصص صغير يرفع اتصال مستمر باستخدام المتغير العالمي. مع هذا كنت قادرا على تحسين مكانة الطلبات في الثانية الواحدة من 350 إلى 1600 (على صفحة بسيطة للغاية مع بعض التحديدات) فقط حفظها في الملف يسمى base.py في أي دليل (مثل postgresql_psycopg2_persistent) وتعيين في الإعدادات

قاعدة البيانات_INGINE إلى projectname.postgresql_psycopg2_persistent.

ملاحظة!!! الكود غير مؤلفة - لا يمكنك استخدامه مع مؤشرات الترابط بيثون بسبب نتائج غير ناضية، في حالة MOD_WSGI، يرجى استخدام وضع Daemon Prefork مع المواضيع = 1


# Custom DB backend postgresql_psycopg2 based
# implements persistent database connection using global variable

from django.db.backends.postgresql_psycopg2.base import DatabaseError, DatabaseWrapper as BaseDatabaseWrapper, \
    IntegrityError
from psycopg2 import OperationalError

connection = None

class DatabaseWrapper(BaseDatabaseWrapper):
    def _cursor(self, *args, **kwargs):
        global connection
        if connection is not None and self.connection is None:
            try: # Check if connection is alive
                connection.cursor().execute('SELECT 1')
            except OperationalError: # The connection is not working, need reconnect
                connection = None
            else:
                self.connection = connection
        cursor = super(DatabaseWrapper, self)._cursor(*args, **kwargs)
        if connection is None and self.connection is not None:
            connection = self.connection
        return cursor

    def close(self):
        if self.connection is not None:
            self.connection.commit()
            self.connection = None

أو هنا هو موضوع آمن، لكن مؤشرات الترابط Python لا تستخدم النوى المتعددة، لذلك لن تحصل على مثل هذا الأداء كما هو الحال مع المرء السابق. يمكنك استخدام هذا واحد مع عملية متعددة واحدة أيضا.

# Custom DB backend postgresql_psycopg2 based
# implements persistent database connection using thread local storage
from threading import local

from django.db.backends.postgresql_psycopg2.base import DatabaseError, \
    DatabaseWrapper as BaseDatabaseWrapper, IntegrityError
from psycopg2 import OperationalError

threadlocal = local()

class DatabaseWrapper(BaseDatabaseWrapper):
    def _cursor(self, *args, **kwargs):
        if hasattr(threadlocal, 'connection') and threadlocal.connection is \
            not None and self.connection is None:
            try: # Check if connection is alive
                threadlocal.connection.cursor().execute('SELECT 1')
            except OperationalError: # The connection is not working, need reconnect
                threadlocal.connection = None
            else:
                self.connection = threadlocal.connection
        cursor = super(DatabaseWrapper, self)._cursor(*args, **kwargs)
        if (not hasattr(threadlocal, 'connection') or threadlocal.connection \
             is None) and self.connection is not None:
            threadlocal.connection = self.connection
        return cursor

    def close(self):
        if self.connection is not None:
            self.connection.commit()
            self.connection = None
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top