Django Postgres 内存泄漏

Fra*_*eli 5 python django postgresql multithreading memory-leaks

我有一个自定义 Django (v 2.0.0) 命令以多线程方式启动后台作业执行器,这似乎给我带来了内存泄漏问题。

该命令可以像这样启动:

./manage.py start_job_executer --thread=1
Run Code Online (Sandbox Code Playgroud)

每个线程都有一个 while True 循环,用于从 PostgreSQL 表中获取作业。

为了继续工作并自动更改状态,我使用了事务:

./manage.py start_job_executer --thread=1
Run Code Online (Sandbox Code Playgroud)

Il 看起来这个 Django 自定义命令分配的内存不断增长。

使用tracemalloc,我尝试通过创建一个检查内存分配的后台线程来查找导致内存泄漏的原因:

# atomic transaction to temporary lock the db access and to
# get the most recent job from db with column status = pending
with transaction.atomic():
    job = Job.objects.select_for_update() \
        .filter(status=Job.STATUS['pending']) \
        .order_by('created_at').first()
    if job:
        job.status = Job.STATUS['executing']
        job.save()
Run Code Online (Sandbox Code Playgroud)

查找到如下日志:

def check_memory(self):
        while True:
            s1 = tracemalloc.take_snapshot()
            sleep(10)
            s2 = tracemalloc.take_snapshot()
            for alog in s2.compare_to(s1, 'lineno')[:10]:
                log.info(alog)
Run Code Online (Sandbox Code Playgroud)

看起来 /usr/local/lib/python3.5/dist-packages/django/db/backends/postgresql/operations.py:222 不释放内存

1 个线程的泄漏很慢,但如果我使用 8 个线程,内存泄漏会更糟:

01.04.20 13:50:06   operations.py:222: size=23.7 KiB (+23.7 KiB), count=66 (+66), average=367 B
01.04.20 13:50:36   operations.py:222: size=127 KiB (+43.7 KiB), count=353 (+122), average=367 B
01.04.20 13:51:04   operations.py:222: size=251 KiB (+66.7 KiB), count=699 (+186), average=367 B
01.04.20 13:51:31   operations.py:222: size=379 KiB (+68.9 KiB), count=1056 (+192), average=367 B
01.04.20 13:51:57   operations.py:222: size=495 KiB (+60.3 KiB), count=1380 (+168), average=367 B
Run Code Online (Sandbox Code Playgroud)

这是 /usr/local/lib/python3.5/dist-packages/django/db/backends/postgresql/operations.py:222 中第 222 行的代码:

01.04.20 13:07:51   operations.py:222: size=68.3 KiB (+68.3 KiB), count=191 (+191), average=366 B
01.04.20 13:08:56   operations.py:222: size=770 KiB (+140 KiB), count=2151 (+390), average=367 B
01.04.20 13:10:07   operations.py:222: size=1476 KiB (+138 KiB), count=4122 (+386), average=367 B

01.04.20 13:36:22   operations.py:222: size=17.3 MiB (+138 KiB), count=49506 (+385), average=367 B

01.04.20 13:48:16   operations.py:222: size=24.5 MiB (+136 KiB), count=69993 (+379), average=367 B
Run Code Online (Sandbox Code Playgroud)

我不知道如何解决这个问题。有什么想法吗?

也发布在这里: https: //code.djangoproject.com/ticket/31419#ticket

我正在考虑为每个需要执行的作业创建一个新进程,一旦完成,内存将被释放,进程本身就会死亡。这可能会起作用,但似乎有点矫枉过正。

提前致谢

更新

我正在使用 Django 2.0,我想更新到 Django 3.0.5(最新的稳定版本),但不幸的是问题仍然存在。

新日志下方:

def last_executed_query(self, cursor, sql, params):
        # http://initd.org/psycopg/docs/cursor.html#cursor.query
        # The query attribute is a Psycopg extension to the DB API 2.0.
        if cursor.query is not None:
            return cursor.query.decode() # this is line 222!
        return None
Run Code Online (Sandbox Code Playgroud)

Sim*_*tte 6

Django 在环形缓冲区中保留对所有已执行查询的引用settings.DEBUG = True

\n

来自DEBUG\xe2\x80\x8b 文档

\n
\n

同样重要的是要记住,当打开运行时DEBUG,Django 将记住它执行的每个 SQL 查询。当您进行调试时,这很有用,但它会快速消耗生产服务器上的内存。

\n
\n

设置DEBUG = False应该可以解决您的问题。

\n

在开发中可能出现问题的情况下擦除环形缓冲区:

\n
from django.db import reset_queries\nif settings.DEBUG:\n    reset_queries()\n
Run Code Online (Sandbox Code Playgroud)\n