繁体   English   中英

查询大型SQL Server表时pymssql/pyodbc性能(cursor.execute)很慢

[英]pymssql/pyodbc performance (cursor.execute) is very slow when querying large SQL Server table

我们对 SQL Server 有一个很大的看法(大约有 500M 记录)。 由于它不适合内存,我正在考虑使用如下所示的 fetchmany 分块处理:

with pymssql.connect(host, user, pass, db) as conn:

    query = f"SELECT * FROM view_name;"

    with conn.cursor() as cursor, futures.ThreadPoolExecutor(3) as executor:
        cursor.execute(query)
        chunk_size = 5000
        data = cursor.fetchmany(chunk_size)

        while data:
            future_rs = executor.submit(process_chunk, data)
            data = cursor.fetchmany(chunk_size)

但是,看起来cursor.execute实际上在我可以调用 fetchmany 之前尝试获取所有行,因为它非常慢。

我对文档的理解是cursor.execute应该只准备查询而不是实现完整结果?

您将如何在可管理的时间内处理如此大的表/视图?

PS:我也试过pyodbc ,这是同样的问题。 正如预期的那样,更改查询以select top 100 * from view_name很快。

好的,经过相当多的调试会话后,我有了一个解决方案。

部分问题被证明是非常缓慢的底层视图。 我误判了这一点,因为像 DBeaver 这样的数据库客户端返回结果的速度非常快(可能是因为它在后台应用分页查询?)。 无论如何,我试图用cursor.fetchmany做的cursor.fetchmany ,我用数据库功能做了。

SQL Server 12 及更高版本使用OFFSETFETCH NEXT具有非常好的分页功能。 所以我的解决方案看起来像这样:

offset = 0
offset_increment = 200000

def get_chunk(cursor, offset):
    query = f"""
            SELECT * FROM table ORDER BY some_col 
            OFFSET {offset} ROWS FETCH NEXT {offset_incriment} ROWS ONLY;
            """
    return cursor.execute(query).fetchall()

with futures.ThreadPoolExecutor(6) as executor:
    chunk = get_chunk(query, offset)

    while chunk:
        executor.submit(process_chunk, chunk)
        offset += offset_increment
        chunk = get_chunk(query, offset)

所以这里的实现是:

  • 将 SQL Server 分页功能与OFFSETFETCH NEXT以获取有限数量的行。
  • 使用多个线程并行处理块。 您还可以并行化 SQL 查询执行部分以使其更快。 这需要做更多的工作,因为您需要知道何时停止。

这是我的解决方案背后的基本思想。 上面的代码只是一个例子,实际上我不得不根据资源使用情况(主要是内存)在我的项目中做更多的调整。 您也可以使用ProcessPoolExecutor进行多处理而不是线程处理。 想法是一样的,代码需要一些更改,因为多处理只需要可挑选的对象。

因此,在块中同时使用分页和处理结果,您可以非常轻松地处理大型表/视图:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM