Python 查询大型SQL Server表时,pymssql/pyodbc性能(cursor.execute)非常慢
我们在SQL Server上有一个大约5亿条记录的大视图。由于它无法放入内存,我考虑使用fetchmany进行分块处理,如下所示:Python 查询大型SQL Server表时,pymssql/pyodbc性能(cursor.execute)非常慢,python,sql-server,performance,pyodbc,pymssql,Python,Sql Server,Performance,Pyodbc,Pymssql,我们在SQL Server上有一个大约5亿条记录的大视图。由于它无法放入内存,我考虑使用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: curso
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,也是同样的问题。如预期的那样,将查询更改为从视图中选择top 100*很快。好的,经过一段时间的调试,我有了一个解决方案 问题的一部分是潜在的观点,这是非常缓慢的。我错误地判断了这一点,因为像DBeaver这样的数据库客户机很可能会很快返回结果,因为它在后台对查询应用分页?。不管怎样,我试图用cursor.fetchmany做的是,我用数据库功能做的 SQLServer12和更高版本使用OFFSET和FetchNext具有非常好的分页功能。因此,我的解决方案如下所示:
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)
因此,这里的实施是为了:
使用带有OFFSET和FETCH NEXT的SQL Server分页功能,只获取有限的行数。
使用多个线程并行处理块。您还可以将SQL查询执行部分并行化以使其更快。这需要更多的工作,因为你需要知道什么时候停止。
这是我的解决方案背后的基本思想。上面的代码只是一个例子,实际上,我不得不在我的项目中根据资源使用情况(主要是内存)进行更多的调优。您还可以使用ProcessPoolExecutor执行多处理而不是线程化。想法是一样的,代码需要一些更改,因为多处理只需要可拾取的对象
因此,在块中同时使用分页和处理结果,您可以非常轻松地处理大型表/视图:您为什么要尝试向应用程序返回5亿行?那是不可用的。如果这是一项工作,那么我预计需要很长时间才能完成5亿行。但是execute方法执行它所声明的,它执行您传递给它的查询。是的,返回100行所需的时间将少于500000000。。。执行意味着执行,就像你认为的那样。你似乎想在这里稍微改变一下单词的定义…看看关于分页的一些想法。这是一个批处理过程,而不是一个应用程序。我需要处理一下这些数据,然后转移到不同的地方。我假设execute是懒惰的,因为有fetchall和fetchmeny方法可用。如果完整的结果已经可用,我认为调用fetchall没有任何意义。它似乎只是将结果转换为列表。我不能为pymssql说话,但pyodbc肯定不会获取.execute上的所有行。使用默认事务隔离READ_COMMITTED的快速Wireshark测试显示-连接到SQL Server:8960字节;从百万行执行SELECT*:82_310字节;fetchmany5_000:314_810字节;Fetchmany 995_000:62_564_304字节这会忽略前200000条记录吗?初始偏移量是否应设置为零?