Django 查询比相同的 Postgres 查询慢 40 倍?

Ric*_*ard 5 django postgresql django-debug-toolbar

我正在使用 Postgres 9.3 运行 Django 1.7,运行runserver. 我的数据库中有大约 2 亿行或大约 80GB 的数据。我正在尝试调试为什么相同的查询在 Postgres 中相当快,但在 Django 中很慢。

数据结构是这样的:

class Chemical(models.Model):
    code = models.CharField(max_length=9, primary_key=True)
    name = models.CharField(max_length=200)

class Prescription(models.Models):
    chemical = models.ForeignKey(Chemical)
    ... other fields
Run Code Online (Sandbox Code Playgroud)

数据库设置了 C 校对规则和合适的索引:

                                   Table "public.frontend_prescription"
  Column       |          Type           |                             Modifiers
 id                | integer                 | not null default nextval('frontend_prescription_id_seq'::regclass)
 chemical_id       | character varying(9)    | not null
 Indexes:
    "frontend_prescription_pkey" PRIMARY KEY, btree (id)
    "frontend_prescription_a69d813a" btree (chemical_id)
    "frontend_prescription_chemical_id_4619f68f65c49a8_like" btree (chemical_id varchar_pattern_ops)
Run Code Online (Sandbox Code Playgroud)

这是我的看法:

def chemical(request, bnf_code):
    c = get_object_or_404(Chemical, bnf_code=bnf_code)
    num_prescriptions = Prescription.objects.filter(chemical=c).count()
    context = {
        'num_prescriptions': num_prescriptions
    }
    return render(request, 'chemical.html', context)
Run Code Online (Sandbox Code Playgroud)

瓶颈是.count(). 称呼。Django 调试工具栏显示花费的时间是 2647 毫秒(在下面的“时间”标题下),但解释部分建议花费的时间应该是 621 毫秒(在底部):

调试工具栏截图

更奇怪的是,如果我直接在 Postgres 中运行相同的查询,它似乎只需要 200-300 毫秒:

# explain analyze select count(*) from frontend_prescription where chemical_id='0212000AA';

QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=279495.79..279495.80 rows=1 width=0) (actual time=296.318..296.318 rows=1 loops=1)
   ->  Bitmap Heap Scan on frontend_prescription  (cost=2104.44..279295.83 rows=79983 width=0) (actual time=162.872..276.439 rows=302389 loops=1)
         Recheck Cond: ((chemical_id)::text = '0212000AA'::text)
         ->  Bitmap Index Scan on frontend_prescription_a69d813a  (cost=0.00..2084.44 rows=79983 width=0) (actual time=126.235..126.235 rows=322252 loops=1)
               Index Cond: ((chemical_id)::text = '0212000AA'::text)
 Total runtime: 296.591 ms 
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:在调试工具栏中,EXPLAIN 语句与 Django 中的实际性能不同。它仍然比 Postgres 中的原始查询慢。

为什么会出现这种差异?我应该如何调试这个/提高我的 Django 应用程序的性能?

更新:这是另一个随机示例:解释为 350 毫秒,渲染时间超过 10,000!帮助,这使我的 Django 应用程序几乎无法使用。

在此处输入图片说明

更新 2:这是另一个慢速(Django 中 40 秒,EXPLAIN 中 600 毫秒...)查询的分析面板。如果我没看错,它表明我认为每个 SQL 调用都需要 13 秒……这是瓶颈吗?

在此处输入图片说明

奇怪的是,分析调用仅对于返回大量结果的查询来说很慢,所以我认为延迟不是适用于每个调用的一些 Django 连接开销。

更新 3:我尝试在原始 SQL 中重写视图,现在有时性能更好,尽管我仍然看到大约一半的时间查询缓慢。(我每次都必须创建并重新创建游标,否则我会收到InterfaceError一条关于游标已死的消息 - 不确定这是否对调试有用。我已经设置了CONN_MAX_AGE=1200。)无论如何,这执行正常,尽管很明显它很容易受到注入等的影响,如下所示:

cursor = connection.cursor()
query = "SELECT * from frontend_chemical WHERE code='%s'" % code
c = cursor.execute(query)
c = cursor.fetchone()
cursor.close()

cursor = connection.cursor()
query = "SELECT count(*) FROM frontend_prescription WHERE chemical_id="
query += "'" + code + "';"
cursor.execute(query)
num_prescriptions = cursor.fetchone()[0]
cursor.close()

context = {
    'chemical': c,
    'num_prescriptions': num_prescriptions
}
return render(request, 'chemical.html', context)
Run Code Online (Sandbox Code Playgroud)

小智 0

很可能Django运行查询时,需要从磁盘读取数据。但是,当您检查查询速度慢的原因时,由于较早的查询,数据已经在内存中了。

最简单的解决方案是购买更多内存,或者更快的 io 系统。