从cx_oracle 7.21 切换到 cx_oracle 8.x 或者 oracledb(测试时为1.2.1最新版)后,在用executemany进行数据导入数据库时,会遇到内存不足错误:
oracledb.exceptions.DatabaseError: DPI-1001: out of memory
报错轨迹看,是来自变量绑定时出现的。通过测试,发现可以降低没批次的行数来解决,就是每次导入行数少,多循环次数导入可以。为什么原来能一次导入而现在不行了呢,查找新的版本的doc,对数据进行测试发现,可以通过setinputsizes 来解决。
通过对数据进一步研究发现,主要是因为不设置时,会有一些大的字段默认分配4000字符(4000*4=16000bytes),字段多的时候,就导致占用较多报错了(我用到的导入的电子表格有100列)。测试时,先看了doc的批量导入优化的内容,采用了对导入的字符串字段预先求出最大长度,然后设置到setinputsizes里,比如各个字段最大长度分别是 5,20,40,10,50...,那么可以设置 setinputsizes(5,20,40,10,50,....),这样不会出现超长占用内存的情况,可以一次导入上万行了。 具体探测字段内存和长度用到的语句:
for var in cursor.bindvars:
print(var.buffer_size,var.size)
以上方式,每次都要计算最大长度,可能略显啰嗦,也可以用简单的方式:
cursor.setinputsizes(1,1,1,1,1)
#或者
cursor.setinputsizes(*(1 for i in range(cols)) ) #cols为列数
这样设置,运行就可达到原来的效果,不知是新版本估计忽略了基础设置,还是bug,也还是想以此来提醒编程时注意相关的设置呢。这样设置按照doc说明,执行中会在发现新的较长的字段时,重新分配内存并有数据重新copy影响性能的情况。
如果是进行库表对库表的传输,可以使用查询源库表的结构直接设置:
cursor.setinputsizes(*(t[1] for t in src_cursor.description))
以上是个人在使用升级时遇到的out of memory的解决情况,如果你也遇到这个问题,希望能给到帮助。