发生 IntegrityError 时内存泄漏

问题描述 投票:0回答:1

我正在开发一个访问 MariaDB 的 API。我使用 SQLAlchemy 进行数据库访问,并在插入数据时遇到奇怪的内存泄漏。当我插入数据并且没有错误时,一切正常。 RAM 消耗会增加,但当事务完成后,它会按预期再次下降。当由于表中的唯一索引而发生 IntegrityError 时,情况并非如此。每次我开始一个新的插入并发生错误时,内存消耗都会上升,并且再也不会下降。在我的项目中,我使用 FastAPI 和 uvicorn 服务器来运行 api,但为了进一步调查,我将问题范围缩小到紧凑的 90 线并具有相同的行为。测试脚本自动创建表,但必须手动创建数据库(etetete)。

我在这里缺少什么?

Python 版本 3.12 新创建的 Docker 容器中的最新 MariaDB

点列表输出:

Package           Version
----------------- -------
greenlet          3.0.3
mariadb           1.1.10
packaging         24.0
pip               24.0
SQLAlchemy        2.0.29
typing_extensions 4.11.0

Python测试脚本:

import time
from sqlalchemy import create_engine,Index,String
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import Session


class Base(DeclarativeBase):
    pass


class Person(Base):
    __tablename__ = "person"
    __table_args__ = (
        Index("age","children","number",unique=True),
    )
    id: Mapped[int] = mapped_column(primary_key=True,autoincrement=True)
    age: Mapped[int]
    children: Mapped[int]
    number: Mapped[int]
    name: Mapped[str] = mapped_column(String(20))
    prename: Mapped[str] = mapped_column(String(20))


def generatePersons(nValues):
    persons = []
    for i in range(0,nValues):
        persons.append(Person(
            age=i,
            children=i,
            number=i,
            name="Smith",
            prename="John"
        ))
    return persons


#DB TEST FUNCTIONS---------------
def testDb(nValues):
    print("Start")
    engine = create_engine("mariadb+mariadbconnector://root:12345@localhost:3306/etetete")
    Base.metadata.create_all(engine)
    s = Session(engine)
    persons = generatePersons(nValues)
    try:
        s.add_all(persons)
        s.commit()
    except Exception as e:
        print("error" + str(e))
        s.rollback()
    finally:
        s.expunge_all()
        s.expire_all()
        s.close()
    engine.clear_compiled_cache()
    engine.dispose()
    print("End")


def testDbContextManager(nValues):
    print("Start")
    engine = create_engine("mariadb+mariadbconnector://root:12345@localhost:3306/etetete")
    Base.metadata.create_all(engine)
    persons = generatePersons(nValues)
    with Session(engine) as s:
        try:
            s.add_all(persons)
            s.commit()
        except Exception as e:
            print("error"+str(e))
    print("End")
#---------------DB TEST FUNCTIONS


def executeTest():
    for i in range(0,100):
        start = time.time()
        # testDb(200000)
        testDbContextManager(200000)
        end = time.time()
        res = end - start
        print("ExecutionTime: " + str(round(res, 2))+"s")
        input("Once again?")
        print()


executeTest()

我尝试了几种不同的方法。有或没有上下文管理器。不同的 SQLAlchemy 版本。使用Python垃圾收集器。首先刷新,最后提交到数据库。 我使用 Windows 任务管理器观察内存消耗,但也使用 python 内存分析器...

python docker sqlalchemy orm mariadb
1个回答
1
投票

好吧,经过数小时的研究和多次不同的尝试,我终于找到了这个问题的解决方案。

SQLAlchemy 没有问题,这是 Python 在异常处理方面的行为。当异常发生时,它会建立一个堆栈回溯,并且由于结果数量较多,异常回溯非常大,因此它会消耗大量内存,并且永远不会清除此堆栈回溯。

解决方案是简单地将其添加到 except 块中以手动删除回溯:

e.__traceback__ = None

我在阅读这篇博文后实现了这个解决方案: https://cosmicpercolator.com/2016/01/13/exception-leaks-in-python-2-and-3/

© www.soinside.com 2019 - 2024. All rights reserved.