8

Pythonで処理した後、MySQLデータベースにXMLデータをインポートしようとしています。簡単にするために、SQLAlchemyを使用してデータベースにアクセスする1つのスクリプトからすべてを実行しています。

XMLファイルには約80,000のエントリがあり、xml.etree.cElementTreeiterparseメソッドを使用して処理し、ノードを使用した後にノードを削除して、メモリ使用量を約20MBに維持します。

SQLAlchemyを含めてデータベースに追加を開始すると、スクリプトがすべてのメモリを使い果たしてOSがメモリを強制終了するまで、メモリ使用量が1秒あたり約10MB増加します。

基本的に私のコードは次のようになります。

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,
    ...
}
4

1 に答える 1

11

これがSQLAlchemyのみのバージョンのコードです。0.7と0.8でテストすると、メモリがリークすることはありません。これは、多くのシナリオでリークが発生しないことを確認するために継続的インテグレーションで12を超えるテストを行っているため、私にとっては驚きではありません。したがって、最初のステップは、このスクリプトがリークしていないことを確認してから、このスクリプトとあなたのスクリプトの間でどのような変更があったかを調べて、リークしているメモリを実際に示すテストケースを作成することです。

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拡張機能の「Decimal」結果プロセッサにリークがありました。

0.6.6-特定の方法で行を選択するために使用された場合、SQLSoup拡張機能に潜在的なリークがあると識別されました(SQLSoupは現在独自のプロジェクトです)

0.5.5-オブジェクトが選択解除されてセッションに戻される場合の潜在的なメモリリークが修正されました

0.5.4-セッションのメモリ使用量が大幅に改善されました。あなたは間違いなくこのバージョンをはるかに超えたいと思っています。

于 2012-12-07T14:16:25.953 に答える