С SQLAlchemy ест оперативной памяти

Я пытаюсь импортировать некоторые XML-данные в мою базу данных MySQL после обработки ее с помощью Python. Чтобы все было просто, я делаю все это из одного скрипта, который использует SQLAlchemy для доступа к моей базе данных.

XML-файл имеет около 80 000 записей, и я обрабатываю его с помощью xml.etree.cElementTree ' s iterparse метод и удалить узлы после их использования, чтобы сохранить использование памяти около 20 Мб.

как только я включаю SQLAlchemy и начинаю добавлять вещи в базу данных, использование моей памяти увеличивается на около 10 МБ в секунду, пока скрипт не исчерпает всю мою память, и ОС не убьет его.

вот в основном, как выглядит мой код:

index = 0

for element in iterate_xml():
    ...

    index += 1

    session.add(Model(**data))

    if index % 1000 == 0:
        session.flush()
        session.commit()

Я не уверен, что еще попробовать. Периодическое .flush() и .commit() помогите немного, но они не исправляют проблему.

является ли SQLAlchemy не правильным инструментом для этой задачи?


я настраиваю SQLAlchemy следующим образом:

Base = declarative_base()
engine = create_engine(config.SQLALCHEMY_DATABASE_URI, echo=False)

Session = sessionmaker(bind=engine, autoflush=False, expire_on_commit=False)
session = Session()

и мой стол выглядит это:

columns = []

for name, datatype in structure.iteritems():
    if isinstance(datatype, int):
        datatype = String(datatype or 20)

    column = Column(name, datatype)
    columns.append(column)

metadata = MetaData(bind=engine)
table = Table('table_name', metadata,
    Column('id', Integer, primary_key=True),
    *columns
)

metadata.drop_all(engine)
metadata.create_all(engine)

class MyTable(Base):
    __tablename__ = 'table_name'
    __table_args__ = {
        'autoload': True,
        'autoload_with': engine
    }

structure - это словарь, который сопоставляет имена столбцов типам данных (он генерируется из XML):

structure = {
    'column_name': SQLAlchemyDataType,
    ...
}

1 ответов


вот версия кода, доступная только для SQLAlchemy. Тестирование в 0.7 и 0.8, это не утечка памяти, что не удивительно для меня, потому что у нас есть более десятка тестов при непрерывной интеграции, чтобы гарантировать, что ничего не утечка во многих сценариях. Поэтому первый шаг-подтвердить, что этот сценарий не протекает для вас, а затем попытаться выяснить, какие изменения между этим сценарием и вашим, чтобы создать тестовый случай, который фактически показывает утечку памяти.

from sqlalchemy import Column, String, Integer, create_engine
from sqlalchemy.orm import Session
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Model(Base):
    __tablename__ = "a"

    id = Column(Integer, primary_key=True)
    data = Column(String)

e = create_engine("sqlite:///somefile.db")

Base.metadata.create_all(e)

session = Session(e)

for index in xrange(10000000):
    session.add(Model(data="data %d" % index))

    if index % 1000 == 0:
        print "flushing... %d" % index
        session.flush()
        session.commit()

важно отметить конечно, те проблемы, где SQLAlchemy просочилась память в прошлом. Вот недавняя история исправленных утечек:

0.7.8 - самые последние. Утечка, исправленная здесь, произошла только при использовании: 1. расширения C, 2. драйвер pyodbc, во время определенных операций выборки результата (не все из них)

0.6.6 - "десятичный" процессор результатов в расширениях C имел утечку.

0.6.6 - расширение SQLSoup было определено как имеющее потенциальную утечку, если используется для выбора строки определенным образом (SQLSoup теперь это собственный проект)

0.5.5-исправлена потенциальная утечка памяти, когда объекты будут распакованы и помещены обратно в сеанс

0.5.4-основные улучшения использования памяти сеанса были сделаны. Вы определенно хотите быть далеко позади этой версии.