6

我运行 Django 1.7 和 Postgres 9.3,运行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

使用 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)

这是我的看法:

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)

瓶颈是.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 

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

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

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

在此处输入图像描述

更新 2:这是另一个慢速(Django 中为 40 秒,EXPLAIN 中为 600 毫秒)查询的 Profiling 面板。如果我没看错,这表明我认为每个 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)
4

2 回答 2

1

这不是您的开发机器上可靠的分析代码(在评论中显示,您的桌面上正在运行各种可能会干扰的东西)。它也不会向您展示在 django-debug-toolbar 处于活动状态时检查运行时的真实性能。如果你对这个东西在野外的表现感兴趣,你必须在你预期的基础设施上运行它,并轻轻地测量它。

def some_view(request):
    search = get_query_parameters(request)
    before = datetime.datetime.now()
    result = ComplexQuery.objects.filter(**search)
    print "ComplexQuery took",datetime.datetime.now() - before
    return render(request, "template.html", {'result':result})

然后,您需要运行几次以预热缓存,然后才能进行任何类型的测量。结果会因设置而异。您可能正在使用需要预热的连接池,postgres 在相同类型的后续查询上更快,django 也可能设置有一些本地缓存,所有这些都需要启动才能确定它是那个查询。

所有的分析工具在报告时间时都没有考虑到它们自己的内省速度减慢,你必须采取一种相对的方法并使用 DDT(或者我最喜欢的这些问题:django-devserver)来识别请求处理程序中持续表现不佳的热点。另一个值得注意的工具:巡边员。设置和维护有点麻烦,但真的很有用。

我负责相当大的设置(数据库大小为数十 GB),还没有看到像这样的简单查询如此糟糕地搁浅。首先找出你是否真的有问题(不仅仅是runserver毁了你的一天),然后使用工具找到那个热点,然后进行优化。

于 2015-10-01T14:31:53.207 回答
0

很可能当 Django 运行查询时,需要从磁盘读取数据。但是当你检查查询为什么很慢时,由于之前的查询,数据已经在内存中。

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

于 2015-03-20T20:12:19.313 回答