使用sqlalchemy会话执行sql DRASTICALLY会减慢执行时间

时间:2013-05-10 18:47:55

标签: python mysql sql sqlalchemy

我有一个相当长的查询(7个连接,现在是7个子选择,因为在原始sql 7子选择相当快 - 我甚至不知道如果我让它运行,7个连接何时完成,但是超过1分钟,相对于.05-.1秒有子选择)

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

我能做些什么吗?

如果您需要更多信息,请告诉我 - 我怀疑这是一个普通的sqlalchemy事情 - 就像sqlalchemy正在建立一个查询计划而不是让mysql这样做?还是...?

编辑:对两者进行解释并且它们看起来完全相同,只是sqlalchemy在extra列中添加了“使用临时;使用filesort”。那是什么减慢了它?我该如何阻止它呢?

编辑2:绝对是sqlalchemy。我尝试使用MySQL游标执行而不是SA会话,并获得相同的.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)

其中GoneAway()是执行SELECT 1以检查连接的方法。

创建会话对象:

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

其中scoped_sessionsessionmaker是sqlalchemy函数。

然后,执行查询的代码:

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

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

4 个答案:

答案 0 :(得分: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)

答案 1 :(得分:4)

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

查看sqlalchemy生成的查询的最简单方法是将echo=True作为create_engine调用的额外参数传递。

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


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

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

这里的实际问题是愚蠢的。对我来说真的真的很蠢。 abarnert实际上很早就猜到了,但它太小了,我和一位同事花了两天才找到。

在控制台版本中,我正在粘贴正确的东西。 在SqlAlchemy版本中,我正在测试AN INT PARAMETER WITH A STRING。

捂脸

谢谢你的帮助,伙计们。你不喜欢这两个角色的错误吗?