2013-07-20 2 views
1

데이터 저장을위한 sqlalchemy 및 MySQL 데이터베이스와 함께 한 응용 프로그램에 Flask를 사용하고 있습니다. Jinja2는 템플리트 작성에 사용됩니다. 개발 중에는 모든 것이 정상 이었지만 더 큰 데이터 세트가 사용되면 많은 데이터가 포함 된 일부 웹 페이지의 렌더링이 매우 느려지고 있습니다. 예를 들어, 사용자 목록은 다음 코드로 처리됩니다.데이터베이스 쿼리시 페이지 속도 향상

def users(): 
    q = Users.query.all() 
    out = [] 
    for i in q: 
     try: 
      something_i_need = Table.query.filter_by(email=i.email).order_by(Table.date).first().id 
     except: 
      something_i_need = 0 
     out.append({ 
       'id': i.id, 
       'last_name': i.last_name, 
       'first_name': i.first_name, 
       'middle_name': i.middle_name, 
       'phone': i.phone, 
       'team': i.team, 
       'status': i.status, 
       'needed': something_i_need, 
       'some_more_data': i.some_more_data 
      }) 
    return render_template('users.html', list_of_users=out) 

데이터는 목록에서 더 많은 필드를 차지하므로 조금 단순화됩니다. 한 번에 2000 명의 사용자를 쿼리하는이 위치에 대한 호출에는 10 초 이상 걸리고 페이지로드에는 10-20 초가 걸립니다. 표는 다음 테마 기능으로 둘러싸여 있으며 first table from this link입니다. 일부 열의 정렬 기능을 사용하지 않도록 설정해도 도움이되지만 여전히 매우 느립니다.

이렇게 궁금한 점이 있습니다.이 과정을 빠르게하거나 템플릿 생성을 최적화하면 어떻게 될까요? 저는 Amazon EC2 인스턴스에서 Python 2.7.3 and mod_wsgi, Flask 0.9, Flask-SQLAlchemy=0.16, Jinja2==2.7, MySQL-python=1.2.4, SQLAlchemy=0.8.1으로 실행하고 있습니다. 하나의 "분명한"해결책은 페이지 정렬을 만들고 100 개 정도의 레코드를 한 번에 반환하는 것입니다. 왜냐하면 목록에 정렬 ​​목적 (날짜가 대부분)이있는 사용자가 모두 있어야하므로 솔루션이 최후의 수단이됩니다.

미리 감사드립니다.

답변

5

문제는 N + 1 개 쿼리를 실행하는 것입니다 (1)가 훨씬 더 할 것 (NUser 테이블에 사용자의 총 번호) :

User.query \ 
     .outerjoin(Table, User.email == Table.email) \ 
     .order_by(User.email, Table.date) 

은 각 사용자의 이메일 주소로 주문한 Table에 모든 항목이 포함 된 모든 사용자를 얻은 다음 날짜 열에 Table을 입력합니다. join 대신 outerjoin을 사용하여 Table에 항목이 없더라도 사용자를 확보 할 수 있습니다.

자, 이것이 우리가 필요로하는 것이 아닙니다. 우리는 실제로 최신 항목이있는 경우 Table에 입력하려고합니다. 이이 작업을 수행 할 수있는 더 좋은 방법은 아마도,하지만 여기에 하위 쿼리와 간단한 예제 :

last_entry = Table.query \ 
        .group_by(Table.email) \ 
        .order_by(Table.date) \ 
        .subquery() 

# Assuming that db is your Flask-SQLAlchemy extension 
results = db.session.query(User, last_entry.c.id) \ 
    .outerjoin(last_entry, User.email == last_entry.c.email) 

그런 다음 당신은 그냥 all를 호출 쿼리를 구체화 할 수 있고 당신이 인종에 떨어져 있습니다 :

return render_template('users.html', list_of_users=results.all()) 
+0

답변을 주셔서 감사합니다. 이제는 합당한 이유없이 수만 건의 요청을 작성하고 있습니다. 그러나이 코드에서 오류가 발생합니다. "AttributeError : '별칭'개체에"결과 = ... "줄에 'id'특성이 없습니다. –

+1

@wont_compile - 사과, 나는'.c. '을 놓쳤습니다 - 우리는 서브 쿼리 객체의'columns' 속성에 접근 할 필요가 있습니다. –

+0

다시 한번 말하지만, 적어도 부분적으로 도움이되었습니다. D 이제 새로운 오류가 발생합니다. "TypeError : 'BaseQuery'개체를 호출 할 수 없습니다." 결과 줄에도. 나는 그것을 어떻게 든 피할 수 있는데, 질의 부분 이후에 (User, last_entry.c.id)를 제거하지만, 테이블의 last_entry를 얻지 못하게되어 다시 뭔가 빠졌습니다. 또한 결과가 클래스 엔진과 동일한 템플릿 엔진에서 어떻게 사용되는지 궁금합니다. 나는 user [id]와 같은 것을 사용하여 데이터를 얻을 수 있었지만, 인보이스를 위해 (아마 쿼리 때문에) 할 수 없었다. –

0

먼저 비동기 적으로 쿼리합니다. 둘째 반환 된 결과를 청크로 표시해야하는 조건부 흐름을 유지합니다. 예를 들어 :

//get lenght of returning cursor 
if(//length of returning cursor is greator thatn 10 or something..) 
{ 
    //Run a loop here and update your UI on UI thread for every 10 values. 
} 
+0

제안 해 주셔서 감사합니다. 불행하게도 사이트가 템플릿 엔진에 의해 생성 되었기 때문에 나는 부분적으로 렌더링하는 방법을 알지 못합니다. 목록이나 다른 인수를 템플릿 파일에 한 번에 전달한 다음 구문 분석하고 반복합니다. 그것들. 비슷하게 쿼리가 서버에서 수행되고 결과가 템플릿 엔진에 전달되기 때문에 비동기로 만드는 방법을 확신 할 수 없습니다. 특히 테이블에 자체 루틴이 있습니다 (jquery). 문서로드시 발생하므로 어떻게 영향을 미칠지 확신 할 수 없습니다. –