使用Django queryset有一个查询日期对象的性能问题

时间:2015-09-26 08:13:24

标签: mysql django performance orm

我正在使用django 1.8.4编写一个webapp。后端使用MySQL 5.6(MyISAM)。最近表记录的数量达到100万,查询所有不同的记录日期需要1-1.5秒。但是使用MySQL客户端需要不到0.001秒。

Django代码

class Model1(models.Model):
    date = models.DateField(db_index=True)

# benchmark code
db_dates = Model1.objects.dates("date", kind="day")

MySQL查询:

SELECT date FROM `table1` group by date ORDER BY `date` ASC

MySQL客户端说:总共620,查询耗时0.0025秒。

更新1

关于@ e4c5提示,我转储了django查询。实际上在查询中有一个类型转换。这是缓慢的根本原因。

{u'time': u'1.989', 
 u'sql': u"SELECT DISTINCT CAST(DATE_FORMAT(`model1_table`.`date`, '%Y-%m-%d 00:00:00') AS DATETIME) AS `datefield` FROM `model1_table` WHERE `model1_table`.`date` IS NOT NULL ORDER BY `datefield` ASC"}

即使我在MySQL客户端中手动运行查询,它也会变慢。我注意到第二个查询进行了类型转换。

类型广告(datedatetime)是否必要?我该如何解决这个问题呢?

无论如何,我已经开了issue here用于跟踪。

2 个答案:

答案 0 :(得分:2)

根本原因

Django在内部生成这样的查询:

SELECT DISTINCT 
  CAST(
    DATE_FORMAT(`model1_table`.`date`, '%Y-%m-%d 00:00:00') AS DATETIME
  ) AS `datefield` 
FROM `model1_table` WHERE `model1_table`.`date` IS NOT NULL 
ORDER BY `datefield` ASC"

显然,所有DATE字段首先被转换为DATETIME,然后使用其日期部分。性能影响是根据记录数量进行扩展。

解决方案

  1. 如@ e4c5所述,我们可以使用
  2. results = set(obj.date for obj in Model1.objects.distinct('date'))

    但这仅适用于PostgreSQL。它不适用于MySQL。我们将收到错误:“此数据库后端不支持 DISTINCT ON字段”。

    1. @BurhanKhalid的回答将检索所有日期对象到客户端,这也很慢。我很快发现我们可以添加distinct来过滤服务器端的结果。
    2. 这是我修改过的版本。

      results = set(Model1.objects.order_by('date').values_list('date', flat=True).distinct())

      现在只需0.004秒即可查询150万条记录中的不同日期字段。酷!

答案 1 :(得分:1)

由于MySQL在django ORM中不支持DISTINCT ON,你可以在客户端进行:

set(Model1.objects.order_by('date').values_list('date', flat=True))