在 pyodbc 中执行 SELECT 查询时,来自 ODBC MS Access 驱动程序的“系统资源超出”错误

Posted

技术标签:

【中文标题】在 pyodbc 中执行 SELECT 查询时,来自 ODBC MS Access 驱动程序的“系统资源超出”错误【英文标题】:"System resource exceeded" Error from ODBC MS Access Driver when executing a SELECT query in pyodbc 【发布时间】:2016-07-05 22:36:26 【问题描述】:

我编写了一个 python 程序,它使用 pyodbc 与 Microsoft Access 数据库进行交互。该数据库包含一个包含井测量值的表格(约 630 万个)。

该程序的基本工作流程是获取化学品清单。然后对于每种化学品,它将使用一个选择查询来查找表中包含该化学品数据的每一口井。对于每个孔,它将使用另一个选择查询来获取一个数据集,其中包含化学孔的所有测量值。

一旦有了这个数据集,它就会计算一些关于它的统计数据,用于为数据集所针对的化学品和孔对创建一个新行。然后将这些新行中的每一行输出到一个新的数据库表中。

请注意,我正在使用 python 的多处理包以允许程序使用更多的 cpu 容量。该程序始终有一个消费者进程,该进程将数据从队列中取出,并将其插入到与输入数据库不同的新数据库中。然后程序有可变数量的生产者进程,它们从队列中获取化学品,处理它们,然后将数据添加到输出队列中。

当我在很长的化学品清单上运行程序时,有时会出错:

Starting: 01027
Failed on 01027 when selecting wells for chemical.
Failed. Wells processed: 371693
Traceback (most recent call last):
  File "C:\Users\jrutledge\Desktop\well_trend_processor_python_2016\python_processor\well_trends.py", line 230, in read_data_sets_into_queue
    raise e

对于 96 种化学品的列表,这种情况至少发生一次(有时更多),但不会发生在 10 种或更少的情况下,不确定界限在哪里。此错误消息的预期原因似乎是运行占用太多内存的查询,但这似乎不是这里的结果。一个原因是,它错误的化学物质在尝试之间不一致。此外,如果我运行包含失败化学品的较小化学品列表,那么它们的处理不会出错。

当我为化学品选择孔时,问题似乎正在发生:

# Find each well that has samples of this chemical
wells_cursor = measurement_db_chemical.cursor()
sql_string = """SELECT DISTINCT 0.Well_ID, PSCODE, STAID, Status
                FROM 0
                LEFT JOIN 1
                ON 0.Well_ID = 1.Well_ID
                WHERE STORE_NUM = ?""".format(
                    well_records_table_name, well_sites_table_name)
try:
    wells_cursor.execute(sql_string, chemical.STORE_NUM)
# Close db connection if error, outer try will close connection
except Exception, e:
    print ('Process : Failed on  when selecting wells for chemical.'.format(process_number, chemical.STORE_NUM))
            raise e

尽管如前所述,此 SQL 查询执行时没有问题的频率远高于导致错误的频率,因此 SQL 中似乎没有错误。但是,这是程序进行的最密集的查询,因此它会超出资源是有道理的。

如果我删除程序的插入部分,此问题仍然存在。通过这种方式,它会做所有它通常会做的事情,但是在计算完统计信息后,它不会将它们插入到新表中。

我认为这可能是由于与数据库的连接打开时间过长,并且不知何故它正在积累垃圾内存或其他东西。这导致我让 python 程序为每种化学品打开一个到数据库的新连接,然后在处理完该化学品后关闭它。但是,问题仍然存在。

值得注意的是,无论我如何运行它,总内存使用率都不会超过 60%,所以我认为这不是问题。

此外,数据库的大小小于 800 mb,这与 MS Access 数据库的 2 GB 大小限制相去甚远。

我只用一个进程运行了这个程序,它工作正常。这使我认为,尽管任何选择查询对于 ODBC 驱动程序来说可能不会太耗费资源,但同时执行多个非常昂贵的查询可能会导致驱动程序达到其资源限制。我现在已经修改了程序并在数据库中执行任何 SQL 查询时添加了 python RLocks,这样一次只能从一个程序读取数据库,这将消除这个问题。昨天成功完成了四个进程的运行,我认为这解决了问题,但今天它在选择井查询中仍然存在相同的错误,即使我只使用一个进程运行它也是如此。

(当我认为这是一个解决方案并现在已将其删除时,我将其发布为答案)

也不是说当使用这种方法时,cpu 使用率永远不会超过 80%,因为进程必须等待彼此查询,并且仍然会出错。这意味着 ODBC 驱动程序与 db 的接口必须对使用有一些编码限制。

你认为是什么导致了这个错误,我应该如何解决它?

如果您想查看更多代码,请告诉我哪个部分(有很多)。

【问题讨论】:

您能否发布完整的回溯,因为 System Resource Exceeded 没有出现在帖子中?此外,这是一个 MS Access 错误,而不是 Python 错误。通常,此消息涉及复杂的查询、NULL 值的处理、保留字和语法项。 另外,作为测试,您能否在 MSAccess.exe 中运行相同的操作(如果您有可用的 Office 程序)?并且 DISTINCTLEFT JOIN 并没有进行太多优化。 @Parfait 我已经在 Access 中运行了良好的选择查询,它工作正常。我不确定如何运行整个操作:连续运行 100 个 Select 查询。除了手动操作之外,这需要一个多小时的点击时间。除了通过 pyodbc 接口对每种化学品都有效的查询之外,它只是不能一次全部有效。 你见过this question吗?你在运行 32 位 Python 吗?如果是这样,是否有可能切换到 64 位 Python(和 64 位 Access 数据库引擎)?另外,您确定运行多个进程真的会加速您的应用程序吗? @GordThompson 实际上有64 bit drivers。我现在就试试。 【参考方案1】:

不幸的是,MS Access 的 System Resources Exceeded 是一条有点模棱两可的消息,它可能与实际 CPU 资源、网络环境或影响引擎性能的低效 SQL 查询或 VBA 模块有关。 p>

但是,您的 SQL 可以进行优化。考虑将DISTINCT 子句替换为在井查询中使用GROUP BY 的聚合查询。 DISTINCT 是大多数 RDMS 中常见的 SQL 性能消耗,需要完整的结果集排序以删除重复项。此外,DISTINCT 往往是一种临时修复,可以弥补数据库设计或计划流程的不足:

sql_string = """SELECT 0.Well_ID, PSCODE, STAID, Status
                FROM 0
                LEFT JOIN 1 ON 0.Well_ID = 1.Well_ID
                WHERE STORE_NUM = ?
                GROUP BY 0.Well_ID, PSCODE, STAID, Status"""\
                .format(well_records_table_name, well_sites_table_name)

如果其他区域触发错误,表设计和查询处理的最佳实践可能会有所帮助。因此,检查其他问题,包括复杂的嵌套子查询; WHEREJOIN 子句中的函数;使用非常宽的表格(在一对多/多对多关系中规范化的标志);以及大型事务,例如大型查询的生成表/追加/更新,Access 会保存结果集的副本以满足回滚需求(有时达到 2 GB 大小)。

访问数据库提示

    试试compacting & repair 偶尔避免膨胀并刷新数据库 查询优化器的统计信息。虽然你的数据库是 800 MB, 众所周知,数据库会在操作期间扩展,并且对于如上所述的大型事务可能会达到文件限制。 除了主键/复合键之外,还可以在表中使用索引。在表格设计视图中,您可以在 字段属性 部分中选择单个字段作为索引,或使用 SQL 的CREATE INDEX。理想情况下,通过此类索引连接表。 将存储的查询保存在 Access 中,这些查询经过预编译和优化以实现最佳计划,并在 Python SELECT * FROM Query1 中调用命名对象,而不是动态运行它的引擎。这是 MS Access 中 VBA 查询与存储查询的常见讨论。甚至可以使用临时表进行计算或迁移到 Python 的 pandas 数据框进行分析。 尝试将后端数据库拆分为多个数据库,以减少单个文件的大小。 Recall Access 可以使用链接表(Python 可以访问)链接到其他 Access 文件,甚至是服务器级别的 RDMS,例如 SQL Server、Oracle、mysql 等。 始终注意Access's limitations,例如表列、查询连接、文件/表大小。毕竟访问是文件级别的 RDMS,而不是基础架构开放的服务器 RDMS。

【讨论】:

我会接受这个答案,因为这个错误消息的问题肯定看起来有很多原因,而且这个答案似乎提供了一些关于接近大多数原因的良好做法。 很好奇,与DISTINCT clause相比,GROUP BY 有帮助吗? 对于那些好奇是什么解决了我的问题的人,可能是各种变化。我认为最大的一个是重新设计程序的流程,以便一次只有一个进程从数据库中读取。不幸的是,这使程序慢得多,因为与所有其他操作相比,从访问中读取速度很慢。但是,它似乎确实限制了连接使用的资源,因此它永远不会引起任何问题。我还通过优化查询和使用最少的连接和游标进行了一些其他小的更改。 抱歉,我没有专门测试GROUP BYDISTINCT的更改表。我认为最大的变化是评论中提到的那个。但是,如果它是一个更优化的查询,那么它可能会有所帮助。

以上是关于在 pyodbc 中执行 SELECT 查询时,来自 ODBC MS Access 驱动程序的“系统资源超出”错误的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 pyodbc 执行保存在 MS Access 中的查询

如果最后一个 impala 语句是 select 语句,如何检查 pyodbc 包?

Pyodbc - 使用 WHERE 子句运行 SQL 查询(语法错误)

基本的pyodbc批量插入

Pyodbc 未检测 SQL 语句中的参数标记(即 - 插入表 SELECT ...)到 Hive 表。这个问题有解决方法吗?

pyodbc连接MSSQL执行SQL语句