Skip to main content
 首页 » 编程设计

Python之如何构建和访问大量数据而不用尽内存或减慢处理速度

2025年05月04日8TianFang

我正在用 Python 编写一个脚本来对专有 ESRI 数据库表中的 QC 数据进行处理。该脚本的目的不是修改无效数据,而只是通过csv文件向用户报告无效数据。我正在使用 ESRI 的 ArcPy 包通过 arcpy.SearchCursor 访问每个单独的记录。 SearchCursor 是访问 ESRI 格式的每个单独记录的唯一方法。

当我滚动浏览表格的每条记录时,我会进行多次 QC 检查以验证特定的业务逻辑。其中一项检查是在特定字段中查找重复数据。这些领域之一可能是几何学。我通过为每个字段创建一个空容器对象来完成此操作,并在检查每条记录时使用以下逻辑。

for field in dupCheckFields: 
    if row.getValue(field) in fieldValues[field]: dupValues.add(row.getValue(idField)) 
    else: fieldValues[field].append(row.getValue(field)) 

上面的代码是我使用的基本逻辑的一个例子。我遇到麻烦的地方是,这些表中的每一个都可能包含 5000 条记录到 1000 万条记录。我要么内存不足,要么性能停止。

我尝试过以下容器类型:集合、列表、字典、ZODB + BList 和 Shelve。

对于内存中的类型(集合、列表、字典),这个过程在开始时非常快,但随着它的进行,它会变得更慢。我们这些类型,如果我在表中有很多记录,我将耗尽内存。使用持久数据类型,我不会耗尽内存,但需要很长时间来处理。

我只在脚本运行时需要数据,所有持久数据文件将在完成后删除。

问题:是否有更好的容器类型来提供大量数据的低内存存储,而访问数据时不会产生大量性能成本?

系统:Win7 64位,Python 2.6.5 32位,4GB内存

预先感谢您的帮助。

编辑:

示例 SQLite 代码:

import sqlite3, os, arcpy, timeit 
 
fc = r"path\to\feature\class" 
 
# test feature class was in ESRI ArcSDE format and contained "." characters separating database name, owner, and feature class name 
fcName = fc.split(".")[-1] 
 
# convert ESRI data types to SQLite data types 
dataTypes = {"String":"text","Guid":"text","Double":"real","SmallInteger":"integer"} 
 
fields = [(field.name,dataTypes[field.type]) for field in arcpy.ListFields(fc) if field.name != arcpy.Describe(fc).OIDFieldName] 
 
# SQL string to create table in SQLite with same schema as feature class 
createTableString = """create table %s(%s,primary key(%s))""" % (fcName,",\n".join('%s %s' % field for field in fields),fields[0][0]) 
 
# SQL string to insert data into SQLite table 
insertString = """insert into %s values(%s)""" % (fcName, ",".join(["?" for i in xrange(len(fields))])) 
 
# location to save SQLite database 
loc = r'C:\TEMPORARY_QC_DATA' 
 
def createDB(): 
    conn = sqlite3.connect(os.path.join(loc,'database.db')) 
    cur = conn.cursor() 
 
    cur.execute(createTableString) 
 
    conn.commit() 
 
    rows = arcpy.SearchCursor(fc) 
 
    i = 0 
    for row in rows: 
        try: 
            cur.execute(insertString, [row.getValue(field[0]) for field in fields]) 
            if i % 10000 == 0: 
                print i, "records" 
                conn.commit() 
            i += 1 
        except sqlite3.IntegrityError: pass 
    print i, "records" 
 
t1 = timeit.Timer("createDB()","from __main__ import createDB") 
 
print t1.timeit(1) 

不幸的是,我无法共享我在这段代码中使用的测试数据,但它是一个 ESRI ArcSDE 地理数据库表,包含大约。 10 个字段和大约。 700 万条记录。

我尝试使用 timeit 来确定这个过程花费了多长时间,但是在处理了 2 个小时之后,只完成了 120,000 条记录。

请您参考如下方法:

如果您将散列存储在(压缩的)文件中,您可以流式传输它们以比较散列以查找重复项。流式传输通常对内存的要求非常低——您可以设置所需的缓冲区,例如,每个散列记录一行。权衡通常是时间,特别是如果您添加压缩,但如果您按某些标准对文件进行排序,那么您可能能够遍历未压缩的流以更快地比较记录。