2014-03-25 309 views
0

cx_Oracle API对我来说非常快,直到我尝试使用CLOB值。性能:如何使用cx_Oracle和executemany()快速插入CLOB?

我做如下:

import time 
import cx_Oracle 

num_records = 100 
con = cx_Oracle.connect('user/[email protected]') 
cur = con.cursor() 
cur.prepare("insert into table_clob (msg_id, message) values (:msg_id, :msg)") 
cur.bindarraysize = num_records 
msg_arr = cur.var(cx_Oracle.CLOB, arraysize=num_records) 
text = '$'*2**20 # 1 MB of text 
rows = [] 

start_time = time.perf_counter() 
for id in range(num_records): 
    msg_arr.setvalue(id, text) 
    rows.append((id, msg_arr)) # ??? 

print('{} records prepared, {:.3f} s' 
    .format(num_records, time.perf_counter() - start_time)) 
start_time = time.perf_counter() 
cur.executemany(None, rows) 
con.commit() 
print('{} records inserted, {:.3f} s' 
    .format(num_records, time.perf_counter() - start_time)) 

cur.close() 
con.close() 
  1. 主要担心的问题是我表现:

    100 records prepared, 25.090 s - Very much for copying 100MB in memory! 
    100 records inserted, 23.503 s - Seems to be too much for 100MB over network. 
    

    有问题的步骤是msg_arr.setvalue(id, text)。如果我评论它,脚本只需要几毫秒即可完成(当然,将空值插入到CLOB列中)。

  2. 其次,在rows数组中添加与CLOB变量相同的引用似乎很奇怪。我在互联网上找到了这个例子,它工作正常,但我做对了吗?

  3. 有没有方法来提高我的情况下的性能?

更新:测试网络吞吐量:一个107 MB的文件通过SMB在11 s内复制到同一主机。但是,网络传输不是主要问题。数据准备需要非常多的时间。

+0

首先要测量您的网络速度。您可能只需使用ftp传输100MB就可以开始。这会给你一个想法,但是oracle使用不同的协议。接下来是跟踪会话。 – steve

+0

@steve,增加了网络速度信息:它可能比这里快两倍,但主要瓶颈是准备CLOB变量。 – greatvovan

+0

行“rows.append((id,msg_arr))#???”是循环的一部分,这看起来不正确。如果这没有帮助,那么可以获得该函数的代码覆盖性能数据。我不认为这个问题与oracle相关,而是与cx_oracle库的实现有关。 – steve

回答

0

奇怪的解决方法(从cx_Oracle邮件列表感谢阿维纳什Nandakumar),但它插入CLOB的时候,极大地提高性能真正的方法:

import time 
import cx_Oracle 
import sys 

num_records = 100 
con = cx_Oracle.connect('user/[email protected]') 
cur = con.cursor() 
cur.bindarraysize = num_records 
text = '$'*2**20 # 1 MB of text 
rows = [] 

start_time = time.perf_counter() 
cur.executemany(
    "insert into table_clob (msg_id, message) values (:msg_id, empty_clob())", 
    [(i,) for i in range(1, 101)]) 
print('{} records prepared, {:.3f} s' 
     .format(num_records, time.perf_counter() - start_time)) 

start_time = time.perf_counter() 
selstmt = "select message from table_clob " + 
      "where msg_id between 1 and :1 for update" 
cur.execute(selstmt, [num_records]) 
for id in range(num_records): 
    results = cur.fetchone() 
    results[0].write(text) 
con.commit() 
print('{} records inserted, {:.3f} s' 
     .format(num_records, time.perf_counter() - start_time)) 

cur.close() 
con.close() 

语义这是不完全一样的,我原来的职位,我希望尽可能简单地展示原理。重点是你应该插入emptyclob(),然后选择它并写下它的内容。