【问题标题】:SQLAlchemy eating RAMSQLAlchemy 吃内存
【发布时间】:2012-12-07 10:30:04
【问题描述】:

我正在尝试在使用 Python 处理后将一些 XML 数据导入我的 MySQL 数据库。为简单起见,我通过一个使用 SQLAlchemy 访问我的数据库的脚本来完成这一切。

XML 文件有大约 80,000 个条目,我使用 xml.etree.cElementTreeiterparse 方法对其进行处理,并在使用它们后删除节点以将内存使用量保持在 20mb 左右。

一旦我包含 SQLAlchemy 并开始将内容添加到数据库中,我的内存使用量就会以每秒 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,
    ...
}

【问题讨论】:

  • session.commit() 之后还要session.explunge_all()
  • @van:这听起来应该有所帮助,但遗憾的是它并没有改变任何东西。内存使用量会暂时保持稳定,但会继续上升。
  • 我想知道这是否是增加内存使用量的XML 部分(而不是SA 部分)。
  • 这听起来不像是 SQLAlchemy 方面的问题。你的模型对象是做什么的?你能提供一个工作的例子吗?如果您查看 gc.get_objects() 的更改会发生什么,实际上添加了哪些类型的对象?
  • @van:我专门重写了 XML 部分以使用很少的 RAM。该脚本需要大约 20 分钟才能运行,并且在遍历 XML 数据时,我的 RAM 使用量保持不变(波动 20mb)。一旦 SQLAlchemy 部分启动,内存使用量就会开始增长。

标签: python memory sqlalchemy


【解决方案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 - 对 Session 的内存使用进行了重大改进。你肯定想远远超过这个版本。

【讨论】:

  • 这几乎是我的代码的样子,除了我使用的是StringIntegerPickleType 和类似于DateTime 的格式。我将一一排除这些数据类型,看看是什么对象导致了问题。感谢您的帮助。
  • 我现在觉得自己很愚蠢。这是我的 XML 解析代码。感谢您花时间写这篇文章,我真的很感激!
猜你喜欢
  • 1970-01-01
  • 2018-04-06
  • 1970-01-01
  • 2012-03-01
  • 2021-03-24
  • 1970-01-01
  • 1970-01-01
  • 2012-12-02
  • 2015-11-10
相关资源
最近更新 更多