【问题标题】:Improve page speed when querying database提高查询数据库时的页面速度
【发布时间】:2013-07-20 18:09:33
【问题描述】:

我正在为我正在开发的一个应用程序使用 Flask,以及用于存储数据的 sqlalchemy 和 MySQL 数据库。 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 包装,虽然禁用某些列上的排序函数有帮助,但它仍然非常慢。

所以,我想知道,我该如何优化这个过程或模板的生成以加快速度?我正在使用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 在 Amazon EC2 实例上运行它。一个“明显”的解决方案,进行分页并一次返回 100 条左右的记录确实有效,因为列表必须包含所有用于排序目的的用户(主要是日期),因此该解决方案将是最后的手段。

提前致谢!

【问题讨论】:

    标签: mysql flask query-optimization jinja2 flask-sqlalchemy


    【解决方案1】:

    您的问题是您正在运行 N+1 个查询(其中 N 是 User 表中的用户总数),其中 1 会做得更好:

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

    将为您提供所有用户及其在Table 中的所有条目,按每个用户的电子邮件地址排序,然后按Table 中的日期列排序。我们使用outerjoin 而不是join 来确保我们获得所有用户,即使他们在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())
    

    【讨论】:

    • 感谢您的回答,现在我在没有任何正当理由的情况下提出了数千个请求是有道理的。但是,我收到此代码的错误,“AttributeError:'Alias' 对象在该“结果 = ...”行中没有属性 'id'。
    • @wont_compile - 抱歉,我错过了.c. - 我们需要访问子查询对象的columns 属性。
    • 再次感谢,这很有帮助,至少部分有用:D 我现在收到一个新错误:“TypeError: 'BaseQuery' object is not callable”也在结果行上。我通过在查询部分之后删除 (User, last_entry.c.id) 以某种方式规避了它,但这导致表中根本没有得到 last_entry,所以我又错过了一些东西。另外,我很好奇结果如何在模板引擎中使用,与类属性相同?我设法通过使用 user[id] 和类似的东西来获取数据,但无法为发票做到这一点(可能是由于查询)。
    • @wont_compile - 是的,还有另一个问题 - 我已更新结果调用以使用正确的 query。
    • 现在没有错误,但我的输出表是空的,但我可以看到它有行,只是没有数据,所以它已填充但我似乎没有正确引用里面的列表。我使用这个: {% for user in users %} user['first_name'], user['last_name'] {% endfor %} 这之前有效,那么您的代码如何影响输出?感谢您的帮助!
    【解决方案2】:

    首先进行异步查询。其次,维护一个条件流,您应该在其中以块的形式显示返回的结果。 例如:

    //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.
     }
    

    【讨论】:

    • 感谢您的建议。不幸的是,由于该站点是由模板引擎生成的,我不知道“部分”呈现它的方法,我所知道的只是一次将一个列表或其他一些参数传递给模板文件,然后解析和迭代那些。查询也类似,因为它在服务器上完成,然后将结果传递给模板引擎,我也不确定如何使其异步,特别是考虑到表本身有一些例程(jquery)文件加载时发生,所以我不确定这会如何影响这些。
    猜你喜欢
    • 1970-01-01
    • 2020-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-14
    • 2016-11-14
    相关资源
    最近更新 更多