我写了一个组件,它对数据库执行一系列动态构造的查询,并最终执行最终查询,其结果被获取。所有这些逻辑都包含在一个发生器中。其他组件可以连接到此生成器并将结果假脱机到文件,在此测试的情况下是制表符分隔的文本文件。这是生成器函数的代码:
def cycle(self, schema, matchprofile, fixedcond):
cursor = self.cursor
cursor.set_schema(schema)
cursor.execute('TRUNCATE TABLE schema.table')
metaatts = self.get_metaattributes(schema)
for condset in matchprofile:
condsql = self.dostuffwith(self, matchprofile, fixedcond)
qry = self.qrybase.replace('<<condset>>', condset).replace('<<condsql>>', condsql)
cursor.execute(qry) # queries performing logic aganst database
cursor.execute(self.finalqry) # select query extracting results
for row in cursor:
yield Row(row, metaatts.copy())
if self.counts:
self.counter.addto(cursor.rowcount)
cursor
是一个子类'cx_Oracle.Cursor',增加了arraysize
和outputtypehandler
,可以将字符串转换为Unicode。从另一个方法调用cycle
,该方法将多个输入模式的输出链接到一个流中。
cycles = itertools.imap(self.cycle, schemas, itertools.repeat(matchprofile), itertools.repeat(fixedcond))
rows = itertools.chain.from_iterable(cycles)
我在Python 2.6上运行它。
我已经运行了整个脚本几十次,在大多数情况下,在数据库架构上完成大约需要11到12分钟。这比预期的要多得多。不可预知的是,在一些尝试中,脚本在大约55秒内完成。这正是我所期望的基于我试图替换的遗留脚本的性能。
由于新工具可以将多个数据库模式作为输入参数,因此我还进行了六次相同模式的测试。记录的执行时间显示问题仅在第一次迭代中发生:
:: 1597 records in 11:33
:: 1597 records in 0:56
:: 1597 records in 0:55
:: 1597 records in 0:55
:: 1597 records in 0:55
:: 1597 records in 0:55
:: total 9582 records in 16:10
我还设法描述了产生合理的运行...
109707 function calls (109627 primitive calls) in 57.938 CPU seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
12 56.154 4.679 56.154 4.680 {function execute at 0x010074B0}
1 0.819 0.819 0.819 0.819 ora.py:194(__init__)
1 0.387 0.387 0.387 0.387 {function parse at 0x010075B0}
1598 0.331 0.000 56.543 0.035 DuplicateDetector.py:219(cycle)
1598 0.118 0.000 0.118 0.000 {method 'writerow' of '_csv.writer' objects}
30295 0.029 0.000 0.029 0.000 {_codecs.utf_8_decode}
1598 0.025 0.000 56.720 0.035 dsv.py:146(generate)
30310 0.022 0.000 0.029 0.000 {method 'encode' of 'unicode' objects}
......而且时间过长。
109707 function calls (109627 primitive calls) in 701.093 CPU seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1598 644.514 0.403 699.827 0.438 DuplicateDetector.py:219(cycle)
12 55.247 4.604 55.248 4.604 {function execute at 0x010084B0}
1 0.783 0.783 0.783 0.783 ora.py:194(__init__)
1 0.283 0.283 0.283 0.283 {function parse at 0x010085B0}
1598 0.121 0.000 0.121 0.000 {method 'write' of '_csv.writer' objects}
30295 0.036 0.000 0.036 0.000 {_codecs.utf_8_decode}
1598 0.025 0.000 700.006 0.438 dsv.py:146(generate)
30310 0.022 0.000 0.028 0.000 {method 'encode' of 'unicode' objects}
30295 0.021 0.000 0.057 0.000 utf_8.py:15(decode)
很明显,在第一种情况下,数据库操作占用大部分执行时间,而后者大部分时间都花在cycle
生成器上。我使用Idle调试器逐步执行此操作,似乎行for row in cursor:
负责执行大约10分钟。我还注意到python.exe进程的内存使用量在此期间不断增加。
现在,问题是该行中发生的情况是相同代码的执行时间如此不同(尽管可重复)?当Cursor用作迭代器时,cx_Oracle在内部执行什么样的操作?我可以在包装代码中犯这样的错误?不可否认,我从来没有见过类似的事情发生在没有使用类和生成器的旧脚本上,只是从光标执行fetchall
。
非常感谢提前。
答案 0 :(得分:0)
不知道这是否适用于您的问题,但是我在昨天为光标设置一个大型数组时遇到了严重的性能问题。
我有两种使用cursor.fetchall()和cursor.fetchmany()的方法。
cursor.fetchall()需要0.1秒才能完成,cursor.fetchmany()需要1.5秒。
经过一些调试后,我注意到我在fetch_many()之前将cursor.arraysize设置为一个较大的值(100.000)。这会导致cxOracle驱动程序为arraysize num行分配mem空间。结合具有大量varchar2(4000)列的查询,总计在每次调用之前分配了一些x * 100MB,然后释放。
也可能是你的问题。