Django persistent database connection

后端 未结 6 511
太阳男子
太阳男子 2020-11-28 18:22

I\'m using django with apache and mod_wsgi and PostgreSQL (all on same host), and I need to handle a lot of simple dynamic page requests (hundreds per second). I faced with

6条回答
  •  醉梦人生
    2020-11-28 18:32

    I made some small custom psycopg2 backend that implements persistent connection using global variable. With this I was able to improve the amout of requests per second from 350 to 1600 (on very simple page with few selects) Just save it in the file called base.py in any directory (e.g. postgresql_psycopg2_persistent) and set in settings

    DATABASE_ENGINE to projectname.postgresql_psycopg2_persistent

    NOTE!!! the code is not threadsafe - you can't use it with python threads because of unexpectable results, in case of mod_wsgi please use prefork daemon mode with threads=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
    

    Or here is a thread safe one, but python threads don't use multiple cores, so you won't get such performance boost as with previous one. You can use this one with multi process one too.

    # 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
    

提交回复
热议问题