4

我有一个相当长的查询(是 7 个连接,现在是 7 个子选择,因为在原始 sql 中,7 个子选择要快得多——我什至不知道如果我让它运行 7 个连接什么时候完成,但比1 分钟与 0.05-.1 秒的子选择)

当我在数据库上运行它时,正如我所说,执行需要 0.05-.1 秒。只需使用即可将session.execute()其减慢到一分钟以上!

有什么我可以做的吗?

如果您需要更多信息,请告诉我——我有点怀疑这是一般的 sqlalchemy 事情——就像 sqlalchemy 正在设置查询计划而不是让 mysql 去做?或者...?

编辑:对两者都进行了解释,它们看起来相同,只是 sqlalchemy 在extra列中添加了“使用临时;使用文件排序”。这就是让它变慢的原因吗?我该如何阻止它这样做?

编辑 2:绝对是 sqlalchemy。我尝试使用 MySQL 游标而不是 SA 会话来执行并获得相同的 0.05 秒运行时间。

编辑 3:

创建我们的引擎的代码:

engine_ro = create_engine(
    config.ro_database_url, #string with username, password, db
    pool_size=config.database_pool_size, #int
    max_overflow=config.database_max_overflow, #int
    pool_timeout=config.database_timeout, # int
    echo=config.database_echo, #False
    echo_pool=config.database_echo, #same as echo #False
    listeners=[GoneAway()] if config.database_use_listeners else None)

whereGoneAway()是一个执行 aSELECT 1来检查连接的方法。

创建会话对象:

SessionRO = scoped_session(sessionmaker(bind=engine_ro, autocommit=False))

wherescoped_sessionsessionmaker是 sqlalchemy 函数。

然后,执行查询的代码:

session = SessionRO()
results = session.execute(sql, params)

编辑 4:如果有人想知道,如果我注释掉这listeners一点,它仍然很慢。同样,如果我只使用sessionmaker没有 scoped_session。

4

4 回答 4

4

这是一个真正的测试套件,用于将 MySQL 游标与 SQLAlchemy 引擎和会话进行比较。请在底部替换您的连接信息和 SQL,然后运行它。让我们知道时间是什么。

import time

def time_thing(fn, description):
    print "Running %s" % description
    now = time.time()
    try:
        ret = fn()
        return ret
    finally:
        spent = time.time() - now
        print "Finished %s, took %d seconds" % (description, spent)

def with_mysqldb(sql):
    import MySQLdb

    conn = MySQLdb.connect(db=DBNAME, user=USERNAME, passwd=PASSWORD, host=HOST)

    def go():
        cursor = conn.cursor()
        cursor.execute(sql)

        # if result fetching is the issue:
        # cursor.fetchall()

        cursor.close()

    time_thing(go, "Executing SQL with MySQLdb cursor")

def _sqla_engine_w_test_connection():
    from sqlalchemy import create_engine
    eng = create_engine(SQLALCHEMY_URL)

    def test():
        conn = eng.connect()
        result = conn.execute("select 1")
        assert result.fetchall()[0] == (1, )

    time_thing(test, "Making a test connection...")

    return eng

def with_sqlalchemy(sql):
    eng = _sqla_engine_w_test_connection()

    def go():
        result = eng.execute(sql)

        # if result fetching is the issue:
        # result.fetchall()

        result.close()
    time_thing(go, "Executing SQL with SQLA engine")

def with_sqlalchemy_session(sql):
    from sqlalchemy.orm import Session

    eng = _sqla_engine_w_test_connection()

    def go():
        sess = Session(eng)

        result = sess.execute(sql)

        # if result fetching is the issue:
        # result.fetchall()

        result.close()

    time_thing(go, "Executing SQL SQLA session")

SQLALCHEMY_URL = "mysql://scott:tiger@localhost/test"
DBNAME = "test"
HOST = "localhost"
USERNAME = "scott"
PASSWORD = "tiger"
SQL = "SELECT 1"

with_mysqldb(SQL)
with_sqlalchemy(SQL)
with_sqlalchemy_session(SQL)
于 2013-05-13T21:07:18.177 回答
4

sqlalchemy没有设置查询计划或其他任何花哨的东西。它只是生成 SQL 并通过 DB-API-2.0 连接发送它。因此,如果您使用生成execute的相同语句显式调用sqlalchemy,它将以完全相同的方式运行。*

查看正在生成什么查询的最简单方法是在调用中作为额外参数sqlalchemy传递。echo=Truecreate_engine

在您的情况下,生成的查询sqlalchemy实际上与您的手动查询不同,因为它使用字符串而不是 int 测试整数参数。


* 这不是 100% 保证的;您必须确保 DB-API-2.0connect函数中的任何连接参数都是相同的,并且您既没有执行任何语句,也没有sqlalchemy执行任何PRAGMA语句。但是您可以使用与测试查询本身相同的方式来测试它们。

于 2013-05-14T19:27:43.750 回答
1

您使用的是哪个 DBAPI?也许尝试将其更改为其他内容。我现在正在使用 PostgreSQL,并且在 pypostgresql 和 psycopg2 之间的性能差异很大(后者要快得多)。

有关 MySQL 可用 DBAPI 的列表,请参阅 SQLAchemy 文档:第 4.1.5 章。

于 2013-05-11T04:52:19.317 回答
0

在控制台版本中,我粘贴了正确的内容。
在 SqlAlchemy 版本中,我正在测试一个带有 string 的 int 参数

于 2013-05-14T18:52:35.883 回答