كيفية تنزيل Oracle LOB الضخم باستخدام cx_Oracle على نظام مقيد بالذاكرة؟

StackOverflow https://stackoverflow.com//questions/12674806

سؤال

أقوم بتطوير جزء من نظام تقتصر فيه العمليات على حوالي 350 ميجابايت من ذاكرة الوصول العشوائي؛نستخدم cx_Oracle لتنزيل الملفات من نظام خارجي للمعالجة.

يقوم النظام الخارجي بتخزين الملفات على هيئة كائنات تخزين كبيرة الحجم (BLOBs)، ويمكننا الحصول عليها عن طريق القيام بشيء مثل هذا:

# ... 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() من الواضح أن تفشل مع MemoryError عندما نحصل على ملف أكبر من 300-350 ميجا بايت، لذلك جربنا شيئًا كهذا بدلاً من قراءته كله مرة واحدة:

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)

لسوء الحظ، ما زلنا نحصل MemoryError بعد عدة تكرارات.من ذلك الوقت lob.read() يأخذ، وحالة نفاد الذاكرة التي نحصل عليها في نهاية المطاف، يبدو كما لو lob.read() يقوم بسحب البايتات (chunk_size + read_size) من قاعدة البيانات كل مرة.وهذا يعني أن عمليات القراءة تستغرق وقتًا O(n) وذاكرة O(n)، على الرغم من أن المخزن المؤقت أصغر قليلاً.

للتغلب على هذه المشكلة، جربنا شيئًا مثل:

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])

يؤدي هذا إلى سحب 2000 بايت (argh) في المرة الواحدة، ويستغرق الأمر إلى الأبد (حوالي ساعتين لملف بحجم 1.5 جيجابايت).لماذا 2000 بايت؟وفقًا لمستندات Oracle، dbms_lob.substr() يخزن قيمة الإرجاع الخاصة به في ملف RAW، والذي يقتصر على 2000 بايت.

هل هناك طريقة ما يمكنني من خلالها تخزين dbms_lob.substr() ينتج عنه كائن بيانات أكبر وربما يقرأ بضعة ميغابايت في المرة الواحدة؟كيف أفعل ذلك باستخدام cx_Oracle؟

هل كانت مفيدة؟

المحلول

أعتقد أن ترتيب الوسيطة في lob.read() معكوس في التعليمات البرمجية الخاصة بك.يجب أن تكون الوسيطة الأولى هي الإزاحة، ويجب أن تكون الوسيطة الثانية هي المبلغ المطلوب قراءته.وهذا من شأنه أن يفسر وقت O(n) واستخدام الذاكرة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top