문제

저는 apache, mod_wsgi 및 PostgreSQL(모두 동일한 호스트에 있음)과 함께 django를 사용하고 있으며 많은 간단한 동적 페이지 요청(초당 수백)을 처리해야 합니다.병목 현상은 django가 지속적인 데이터베이스 연결을 갖고 있지 않고 각 요청(거의 5ms가 소요됨)에 다시 연결한다는 문제에 직면했습니다.벤치마크를 수행하는 동안 지속적인 연결을 사용하면 거의 500r/s를 처리할 수 있는 반면 연결이 없으면 50r/s만 얻을 수 있다는 것을 얻었습니다.

누구든지 조언이 있나요?영구 연결을 사용하도록 django를 수정하는 방법은 무엇입니까?또는 Python에서 DB로의 연결 속도를 높입니다.

미리 감사드립니다.

도움이 되었습니까?

해결책

장고 1.6 추가했습니다 지속적인 연결 지원(django 1.9에 대한 문서 링크):

지속적인 연결은 각 요청에서 데이터베이스에 대한 연결을 다시 설정하는 오버 헤드를 피합니다.그들은 에 의해 제어됩니다. CONN_MAX_AGE 매개 변수의 최대 수명을 정의합니다. 연결.각 데이터베이스에 대해 독립적으로 설정할 수 있습니다.

다른 팁

노력하다 PgBouncer - PostgreSQL용 경량 연결 풀러입니다.특징:

  • 연결을 회전할 때 여러 수준의 잔인함:
    • 세션 풀링
    • 트랜잭션 풀링
    • 명세서 풀링
  • 낮은 메모리 요구사항(기본적으로 연결당 2k)

Django 트렁크에서 편집 django/db/__init__.py 다음 줄을 주석 처리합니다.

signals.request_finished.connect(close_connection)

이 신호 처리기는 요청이 있을 때마다 데이터베이스와의 연결을 끊습니다.이 작업으로 인해 어떤 부작용이 있을지는 모르겠지만 요청이 있을 때마다 새 연결을 시작하는 것은 의미가 없습니다.아시다시피 성능이 저하됩니다.

나는 지금 이것을 사용하고 있지만, 문제가 있는지 확인하기 위해 전체 테스트를 수행하지 않았습니다.

왜 모든 사람들이 여기에 새로운 백엔드나 특별한 연결 풀러 또는 기타 복잡한 솔루션이 필요하다고 생각하는지 모르겠습니다.이것은 매우 간단해 보이지만 처음에 이 작업을 수행하게 만든 몇 가지 모호한 문제가 있다는 것은 의심하지 않습니다. 이는 보다 현명하게 처리되어야 합니다.아시다시피 모든 요청에 ​​대한 5ms 오버헤드는 고성능 서비스의 경우 상당히 많습니다.(나한테 걸리는데 150ms--아직 이유를 찾지 못했습니다.)

편집하다:또 다른 필요한 변경 사항은 django/middleware/transaction.py에 있습니다.두 개의 transaction.is_dirty() 테스트를 제거하고 항상 commit() 또는 Rollback()을 호출하십시오.그렇지 않으면 데이터베이스에서만 읽는 경우 트랜잭션을 커밋하지 않으므로 닫아야 하는 잠금이 열린 상태로 유지됩니다.

작은 것을 만들었어요 장고 패치 sqlalchemy 풀링을 통해 MySQL과 PostgreSQL의 연결 풀링을 구현합니다.

이것은 생산에 완벽하게 작동합니다. http://grandcapital.net/ 오랜 기간 동안.

패치는 주제를 조금 검색한 후에 작성되었습니다.

부인 성명:나는 이것을 시도하지 않았습니다.

나는 사용자 정의 데이터베이스 백엔드를 구현해야 한다고 생각합니다.연결 풀링을 사용하여 데이터베이스 백엔드를 구현하는 방법을 보여주는 몇 가지 예가 웹에 있습니다.

연결이 풀로 반환될 때 네트워크 연결이 열린 상태로 유지되므로 연결 풀을 사용하는 것이 좋은 솔루션이 될 수 있습니다.

  • 이 게시물 Django를 패치하여 이를 수행합니다(코멘트 중 하나는 핵심 django 코드 외부에 사용자 정의 백엔드를 구현하는 것이 더 낫다고 지적합니다).
  • 이 게시물 사용자 정의 DB 백엔드의 구현입니다.

두 게시물 모두 MySQL을 사용합니다. 아마도 Postgresql에서도 비슷한 기술을 사용할 수 있을 것입니다.

편집하다:

  • Django Book에서는 다음을 사용하여 Postgresql 연결 풀링을 언급합니다. pgpool (지도 시간).
  • 누군가 게시했습니다. 패치 연결 풀링을 구현하는 psycopg2 백엔드의 경우.자신의 프로젝트에서 기존 백엔드의 복사본을 만들고 패치하는 것이 좋습니다.

전역 변수를 사용하여 지속적인 연결을 구현하는 작은 사용자 정의 psycopg2 백엔드를 만들었습니다.이를 통해 초당 요청 수를 350에서 1600으로 향상시킬 수있었습니다 (선택이 거의없는 매우 간단한 페이지에서) 라는 파일에 저장하십시오. base.py 임의의 디렉토리(예:postgresql_psycopg2_persist) 및 설정에서 설정

DATABASE_ENGINE을 프로젝트 이름.postgresql_psycopg2_percious로

메모!!!코드는 스레드로부터 안전하지 않습니다. 예상치 못한 결과 때문에 Python 스레드와 함께 사용할 수 없습니다. mod_wsgi의 경우 스레드=1로 prefork 데몬 모드를 사용하세요.


# 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