我注意到使用django connection.cursor与使用模型接口之间存在巨大的时间差异,即使是使用小型查询集也是如此。 我已经使模型接口尽可能高效,使用了values_list,因此没有构造任何对象等。以下是测试的两个功能,不要介意西班牙语名称。
def t3():
q = "select id, numerosDisponibles FROM samibackend_eventoagendado LIMIT 1000"
with connection.cursor() as c:
c.execute(q)
return list(c)
def t4():
return list(EventoAgendado.objects.all().values_list('id','numerosDisponibles')[:1000])
然后使用函数计时(用time.clock()自行制作)
r1 = timeme(t3); r2 = timeme(t4)
结果如下: t3和t4为0.00180384529631和0.00493390727024
只是为了确保查询并执行相同的操作:
connection.queries[-2::]
收率:
[
{u'sql': u'select id, numerosDisponibles FROM samibackend_eventoagendado LIMIT 1000', u'time': u'0.002'},
{u'sql': u'SELECT `samiBackend_eventoagendado`.`id`, `samiBackend_eventoagendado`.`numerosDisponibles` FROM `samiBackend_eventoagendado` LIMIT 1000', u'time': u'0.002'}
]
正如你所看到的,两个确切的查询,返回两个精确列表(执行r1 == r2返回True),采用完全不同的时序(差异随着更大的查询集变大),我知道python很慢,但是django在幕后做了这么多工作,使查询更慢? 另外,为了确保,我已经尝试首先构建queryset对象(在计时器之外)但结果是相同的,所以我100%确定额外的时间来自获取和构建结果结构。 我也尝试在查询结束时使用iterator()函数,但这对它们都没有帮助。 我知道差别很小,两者都执行速度非常快,但这是与apache ab相关的,并且这个最小的差异,当有1k个并发请求时,会使日和光。
顺便说一句,我使用django 1.7.10和mysqlclient作为db连接器。
编辑:为了进行比较,使用11k结果查询集进行相同的测试,差异变得更大(比第一个慢大约2.6倍慢的3倍)
r1 = timeme(t3); r2 = timeme(t4)
0.0149241530889
0.0437563529558
EDIT2:另一个有趣的测试,如果我实际上将queryset对象转换为它的实际字符串查询(使用str(queryset.query)),并在原始查询中使用它,我会得到相同的良好性能作为原始查询,通过使用queryset.query字符串的execption有时会给我一个实际的无效SQL查询(即,如果查询集有一个日期值的过滤器,日期值不会转义为'&# 39;在字符串查询中,在使用原始查询执行时出现sql错误,这是另一个谜。)
- EDIT3:
通过代码,似乎区别在于如何检索结果数据,对于原始查询集,它只是调用iter(self.cursor)
我相信当使用C实现的连接器将在C代码中运行时(如iter也是内置的),而ValuesListQuerySet实际上是一个带有yield元组(行)语句的循环的python级别,这将非常慢。我想在这件事上没有什么可以做与原始查询集相同的性能:'(。
如果有人感兴趣,那么慢循环是这样的:
for row in self.query.get_compiler(self.db).results_iter():
yield tuple(row)
- 编辑4:我已经提供了一个非常hacky代码,可以将值列表查询集转换为可用数据以发送到原始查询,具有与运行原始查询相同的性能,我想这是非常糟糕的,只能用于mysql,但是,加速非常好,同时允许我保持模型api过滤等。你怎么看? 这是代码。
def querysetAsRaw(qs):
q = qs.query.get_compiler(qs.db).as_sql()
with connection.cursor() as c:
c.execute(q[0], q[1])
return c
答案 0 :(得分:2)
答案很简单,更新到django 1.8或更高版本,这改变了一些在性能上不再出现此问题的代码。