我编写了一个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种化学物质的列表,这种情况至少发生一次(有时甚至更多),但不是十次或更少,不确定截止的位置。此错误消息的预期原因似乎是运行占用太多内存的查询,但这似乎不是此处的结果。其中一个原因是,它出错的化学物质在尝试之间并不一致。此外,如果我运行一个包含失败化学品的较小化学品清单,那么它们的处理没有错误。
当我选择化学品的孔时,问题似乎正在发生:
# 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程序为每个化学品打开一个与db的新连接,然后在处理完该化学品时关闭它。但问题仍然存在。
值得注意的是,无论我如何运行它,总内存使用率都不会超过60%所以我认为这不是问题所在。
此外,数据库的大小还小于800 MB,而MS Access数据库的大小限制为2 gb。
我只使用一个进程运行该程序,但它运行正常。这让我认为,尽管任何人选择查询对于ODBC驱动程序来说可能不会太昂贵,但是同时执行多个非常昂贵的查询并且可能导致驱动程序达到其资源限制。我现在修改了程序并在数据库中执行任何SQL查询时添加了python RLocks,这样一次只能有一个程序从db中读取,这样就可以解决这个问题。昨天有四个进程的运行成功完成,我认为这解决了问题,但今天它仍然在选择井查询中有相同的错误,即使我只用一个进程运行它。
(当我认为这是一个解决方案并且现已将其删除时,我将此作为答案发布)
当使用此方法时,cpu的使用率从未超过80%,因为进程必须等待彼此查询,并且仍然存在错误。这意味着ODBC驱动程序与db的接口必须具有一些使用的编码限制。
您认为导致此错误的原因是什么?我应该如何解决此错误?
如果您想查看更多代码,请告诉我哪个部分(有很多部分)。
答案 0 :(得分:2)
不幸的是,MS Access' 超出系统资源是一个有点模棱两可的消息,它可能与实际CPU资源,网络环境或影响引擎性能的低效SQL查询或VBA模块有关。
但是,您的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)
如果其他区域触发错误,表格设计和查询处理中的最佳做法可能会有所帮助。因此,请检查其他问题,包括复杂的嵌套子查询; WHERE
和JOIN
条款中的函数;使用非常宽的表格(在一对多/多对多关系中标准化的标志);和大型事务,如make-table / append / update of big queries,Access保存结果集的副本以满足回滚需求(有时达到2 GB大小)。
访问数据库提示
CREATE INDEX
。理想情况下,通过这些索引连接表格。SELECT * FROM Query1
中的命名对象,而不是动态运行它的引擎。这是MS Access中VBA查询与存储查询的常见讨论。甚至可以使用临时表进行计算或迁移到Python的pandas数据帧进行分析。