Python在sqlite3数据库中插入大元素 - 内存错误
我在用Python做数值模拟,生成的数据对象比较大,大约有200MB。我把这些数据写入一个sqlite3数据库。最近我把分辨率提高了大约20%,结果在尝试插入数据库时出现了内存错误。之前用较低分辨率时一切都很好。下面是我的代码片段:
def write_to_db(self, filename, dataObject, name) :
connection = sqlite.connect(filename)
cursor = connection.cursor()
cursor.execute("CREATE TABLE pulses (ID INTEGER PRIMARY KEY, name STRING, data BLOB)")
cursor.execute("INSERT INTO pulses(name, data) VALUES (?, ?)", (dataObjectName, sqlite.Binary(pickle.dumps(dataObject))))
connection.commit()
connection.close()
我是在winXP系统下工作,电脑有1GB的内存和3GB的交换空间(而且没有收到需要扩展交换空间的提示),使用的是Python 2.6。
谢谢你的帮助。
Tim2 个回答
你在评论中提到可能的替代存储方式。如果你存储的数据占用了可用内存的五分之一,我建议你要非常小心地管理内存,因为在内存不够用之前,数据不能被复制太多;而且这样做会影响性能。你可以尝试用sqlite来处理BLOB(大二进制对象),因为它们可能会很大。
看起来你是在用sqlite作为一个简单的键值存储。你有没有考虑过使用平面文件?如果你需要原子性(也就是要么全部成功,要么全部失败),你仍然可以用sqlite数据库来定义哪些平面文件是有效的。你可以在所有平面文件都写入干净后,再提交到数据库,这样就可以安全地处理文件,只有在数据库中提交了相应的删除后,才能删除平面文件。
要让这个工作顺利进行,你需要一种机制来把你的dataObject
序列化成Python中的文件类型对象。如果你传给Pickle一个文件类型的对象,它可以帮你做到这一点,但我怀疑这样做的效率可能还是不高。
你说你在做数值模拟;你知道numpy吗?numpy数组支持一个tofile
函数,这比Pickle更高效,如果你还没有使用它,可能会让你的模拟性能大幅提升。
注意,XP系统在增加交换空间时只会逐步增加。如果你的程序突然需要用到更多的内存,而交换空间又不够,就会出现内存错误。
SQLite可以很顺利地处理大小达到1GB的二进制大对象(blob),通常你可以使用到2GB。不过在32位的程序中,你会遇到地址空间不足的问题。
一般来说,对于较大的数据,建议把这些大数据存储在文件中,然后把文件名存到数据库里,但这样会增加你的工作量。
你可以通过以下方法解决你眼前的问题:
切换到64位系统。微软有出售一个Windows 7家庭装包,里面包含3个XP/Vista的升级,价格大约是150美元(市场价130美元),这样你可以升级多台机器。通过这种方式,你可以从32位的XP切换到64位的Win 7。仅仅这样做就能立即解决你的问题,即使你不改变内存等配置。
在pickle调用中加上-1,这样可以让它使用最新的pickle协议,使用二进制格式而不是默认的ascii编码。这样你得到的数据会更少。想了解更多关于协议版本和支持这些版本的Python版本的信息,可以查看文档。
还可以对pickle的数据进行压缩,比如使用bz2.compress(pickle.dumps(obj, -1))。
你遇到问题的最可能原因是地址空间不足。32位的程序通常只能同时处理2GB的数据,而各种可执行文件、共享库、每个线程的栈、SQLite缓存等也会占用这部分空间。你需要仔细关注你所有的数据项以及它们的生命周期。主动调用del和gc.collect()在你不再需要它们时,可以帮助减少同时使用的数据量。