2012-10-01 28 views
6

Tôi đang phát triển một phần của một hệ thống mà các quy trình được giới hạn ở khoảng 350MB RAM; chúng tôi sử dụng cx_Oracle để tải xuống tệp từ hệ thống bên ngoài để xử lý.Làm thế nào để tải xuống Oracle LOB lớn với cx_Oracle trên hệ thống bộ nhớ bị hạn chế?

Các bên ngoài các cửa hàng hệ thống file dưới dạng BLOB, và chúng ta có thể lấy chúng làm một cái gì đó như thế này:

# ... set up Oracle connection, then 
cursor.execute(u"""SELECT filename, data, filesize 
        FROM FILEDATA 
        WHERE ID = :id""", id=the_one_you_wanted) 
filename, lob, filesize = cursor.fetchone() 

with open(filename, "w") as the_file: 
    the_file.write(lob.read()) 

lob.read() rõ ràng là sẽ thất bại với MemoryError khi chúng ta đánh một tập tin lớn hơn 300-350MB, vì vậy chúng tôi đã cố gắng một cái gì đó như thế này thay vì đọc nó tất cả cùng một lúc:

read_size = 0 
chunk_size = lob.getchunksize() * 100 
while read_size < filesize: 
    data = lob.read(chunk_size, read_size + 1) 
    read_size += len(data) 
    the_file.write(data) 

Thật không may, chúng tôi vẫn nhận được MemoryError sau nhiều lần lặp lại. Từ thời điểm lob.read() đang thực hiện và điều kiện hết bộ nhớ chúng tôi nhận được, có vẻ như lob.read() đang kéo (chunk_size + read_size) byte từ cơ sở dữ liệu mỗi lần. Nghĩa là, các lần đọc đang lấy thời gian O (n) và O (n), mặc dù bộ đệm hơi nhỏ hơn một chút.

Để làm việc xung quanh này, chúng tôi đã thử một cái gì đó như:

read_size = 0 
while read_size < filesize: 
    q = u'''SELECT dbms_lob.substr(data, 2000, %s) 
      FROM FILEDATA WHERE ID = :id''' % (read_bytes + 1) 
    cursor.execute(q, id=filedataid[0]) 
    row = cursor.fetchone() 
    read_bytes += len(row[0]) 
    the_file.write(row[0]) 

này kéo 2000 byte (argh) tại một thời điểm, và sẽ mãi mãi (một cái gì đó giống như hai giờ cho một tập tin 1.5GB). Tại sao 2000 byte? Theo tài liệu của Oracle, dbms_lob.substr() lưu trữ giá trị trả về của nó trong một RAW, được giới hạn ở 2000 byte.

Có cách nào tôi có thể lưu trữ kết quả dbms_lob.substr() trong đối tượng dữ liệu lớn hơn và đọc có thể một vài megabyte tại một thời điểm không? Làm thế nào để làm điều này với cx_Oracle?

Trả lời

1

Tôi nghĩ rằng thứ tự đối số trong lob.read() được đảo ngược trong mã của bạn. Đối số đầu tiên phải là giá trị bù trừ, đối số thứ hai sẽ là số tiền cần đọc. Điều này sẽ giải thích việc sử dụng thời gian và bộ nhớ O (n).

+0

Ồ, tôi không thể tin rằng đó là điều sai. * facepalm * Cảm ơn! –

Các vấn đề liên quan