如何调试 Django+Postgres 中的慢查询

如何调试 Django+Postgres 中的慢查询

我从 Django 进行的数据库查询开始需要 1-2 秒,我很难弄清楚原因。网站不是很大,每秒大约有 1-2 个请求(这些请求会命中 Django;静态文件仅由 nginx 提供。)

让我困惑的是,我可以使用调试模式在 Django shell 中复制缓慢的情况。但是当我在 SQL 提示符下发出完全相同的查询时,它们很快。查询返回大约需要一秒钟,但当我检查 connection.queries 时,它报告的时间不到 10 毫秒。

下面是一个例子(来自 Django shell):

>>> p = PlayerData.objects.get(uid="100000521952372")
>>> a = time.time(); p.save(); print time.time() - a
1.96812295914
>>> for d in connection.queries: print d["time"]
... 
0.002
0.000
0.000

我怎样才能知道这些额外的时间都花在了哪里?

我在守护进程模式下使用 Apache+mod_wsgi,但这也只发生在 django shell 上,所以我认为这与 apache 无关。

答案1

我遇到过这个问题,但 MySQL 除外。有一个管理页面需要很长时间才能加载(15 秒),但最慢的查询只需要几秒钟。经过几个小时的调试,我发现了问题所在。

mysql 数据库服务器确实在几秒钟内返回了查询结果,但查询尚未完全处理完毕(通过生命周期结束,包括清理),直到结果已经返回。下面逐步解释发生了什么:

  1. 昂贵的查询命中数据库
  2. MySQL 服务器在几秒钟后返回查询结果,但继续进行清理工作(就 mysql 服务器而言,该查询尚未达到使用寿命)。django 调试工具栏(和 django.db.connection.queries)中显示的此查询所用时间在结果返回后停止计时。
  3. Django 返回结果并继续加载页面,同时,在背景中,mysql 继续清理使用的临时表。
  4. Django 准备另一个 SQL 查询并将其发送到服务器。
  5. MySQL 尚未完成临时表的清理,因此......
  6. Django 必须坐下来等待。Django 使用与以前相同的 mysql db 连接,并且 mysql 不会让同一连接运行另一个查询,直到前一个连接达到使用寿命结束(包括清理)。

我通过在 mysql 命令提示符中运行“show full processlist;”来解决这个问题。对于所有未完成的查询,它显示查询所花费的时间(到目前为止)、状态和实际查询文本。在启动昂贵的查询三到四秒后,它将开始显示“删除临时表”作为状态。在查询已经将结果返回给 Django 后,它会显示此状态长达 7 秒。因此,显然 MySQL 清理查询所花的时间比实际返回结果所花的时间要长得多。在我看来,这正是问题所在。

有趣的是,“清理”时间并没有出现在有问题的查询的查询时间中也不后续查询的开始实际上已被延迟。

不确定这是否是你的问题,但我想值得研究一下。

答案2

Django 的一个很棒的插件是 Django 调试工具栏 (github.com/robhudson/django-debug-toolbar)。它会显示您在每次页面加载时进行的查询(以及每个查询的解释输出)。

答案3

您使用的是哪个版本的 Django?稳定版还是主版本?1.x?0.9x?只有一件事您可以测试。您说的“精确查询”是什么意思。您是否在 Django 中使用自定义 SQL 查询,或者您是否已获取查询并在 SQL shell 上运行它。

另一个“猜测”:数据库是否在同一台机器上?也许您遇到了网络延迟问题。当您打开 SQL Prompt 时,您已经连接到数据库服务器,而 Django 必须建立连接?

就像我说的,这只是一个猜测。我自己也遇到过网络问题,因为我们公司有防火墙/路由。但延迟没有你这么高。

相关内容