【问题标题】:SQLAlchemy leaks memorySQLAlchemy 泄漏内存
【发布时间】:2017-12-30 12:55:37
【问题描述】:

以下代码泄漏了我系统上的内存:

def test():
    for u in users:
        session.flush()
        some_list = u.some_list

users 定义为

users = session.query(User).yield_per(500000)

some_list 在 User 模型中定义为

some_list = relationship("SomeThing", backref="user")

如您所见,运行session.flush() 不会释放已使用的内存。就此而言,循环末尾的del some_list 也没有。如果我删除 some_list = u.some_list 行,内存泄漏就会消失。

编辑:根据要求,这里是相关文件的完整代码。我认为它不会有太大帮助,因为我可以仅使用函数 test() 重现问题。

from redis import Redis
from pottery import RedisDict
import json

from anime_db import generate_session, User

redis = Redis.from_url('http://localhost:6379')
toshokan = RedisDict(redis=redis, key='toshokan')

session = generate_session(User)
users = session.query(User).yield_per(5000)


users = users[200000:]

def process():
    c = 0
    for u in users:
        session.flush()
        if c % 10000 == 0:
            print('PROCESSED {} USERS'.format(c))
        c += 1
        al_db = u.animelist
        name = u.name
        if len(al_db) == 0 or name in toshokan:
            continue
        al = []
        for a in al_db:
            al.append((a.anime_id, a.status, a.num_watched, a.score))
        toshokan[name] = json.dumps(al)
        del al_db, name, al

def test():
    c = 0
    for u in users:
        session.flush()
        if c % 1000 == 0:
            print('something')
        c += 1
        name = u.name
        al_db = u.animelist

相关的数据库模型定义如下:

class AnimeEntry(Base):
    __tablename__ = 'list_entries_anime'

    uid = Column(Integer, Sequence('anime_list_entry_id_seq'), primary_key=True, autoincrement=True)
    anime_id = Column(Integer, ForeignKey("anime.uid"), nullable=False)
    user_id = Column(Integer, ForeignKey("users.uid"), nullable=False)
    status = Column(Integer)
    num_watched = Column(Integer)
    score = Column(Integer)
    added = Column(DateTime(timezone=True), default=func.now())

    def __repr__(self):
        return "<AnimeEntry(uid='%s', title='%s', user='%s')>" % (self.uid, self.anime_id, self.user_id)

class User(Base):
    """A user entry."""
    __tablename__ = 'users'

    uid = Column(Integer, Sequence('user_id_seq'), primary_key=True, autoincrement=True)
    name = Column(String, unique=True)

    joined = Column(DateTime(timezone=True), default=func.now())
    animelist = relationship("AnimeEntry", backref="user")

编辑:停止循环不会停止内存使用,但之后运行 users = [] 会。所以这个问题与session.query(User).yield_per(5000)有关。

【问题讨论】:

  • 为什么你认为session.flush() 会释放内存?为此,您需要session.expunge_all()
  • 我在其他地方被告知 flush() 可以工作;它没。就此而言,expunge_all() 也没有。
  • 您如何观察到内存泄漏?你的其余代码是做什么的?
  • 系统内存使用率稳步增长,不受控制。我正在运行的唯一代码是上述代码示例中的代码。中断该函数会清除内存。
  • 我知道这个过程令人沮丧,但当我无法运行您的代码时,我试图猜测您的问题同样令人沮丧。自包含只是意味着我可以运行您的代码并重现您的问题。它确实意味着只包含您认为可能导致问题的所有代码。你不能包括你的数据?包括一个可以重现您的问题的虚拟数据集。使用 PostgreSQL?看看它是否也能用 SQLite 重现。这些都是有价值的信息,如果您提供这些信息,您将获得更好的帮助。

标签: python memory-leaks sqlalchemy


【解决方案1】:

似乎yield_per() 以某种方式导致了不合理的内存使用。我已经用以下代码替换了那部分代码:

def page_query(q):
    offset = 0
    while True:
        r = False
        for elem in q.limit(10000).offset(offset):
           r = True
           yield elem
        offset += 10000
        if not r:
            break

users = page_query(session.query(User))

正如人们所期望的那样。我仍然不知道为什么 yield_per 的行为确实如此;我以前用过它,它在这种情况下表现得很好。

【讨论】:

    猜你喜欢
    • 2021-02-06
    • 2013-01-25
    • 2011-07-30
    • 1970-01-01
    • 1970-01-01
    • 2011-10-08
    • 2013-01-20
    • 2011-10-31
    • 2019-08-10
    相关资源
    最近更新 更多