Django和MySQL发生奇怪的错误

时间:2019-03-12 09:35:09

标签: mysql django django-orm

我有两段代码,它们都执行相同的操作,但是其中一部代码花费50秒,而另一段代码花费不到5秒。

模型

class Device(models.Model):
    device_uid = models.CharField(max_length=50, unique=True, null=False)


class DeviceReadings(models.Model):
    device = models.ForeignKey(Device)
    value = models.FloatField(default=0)
    created_dt = models.DateTimeField()

    class Meta:
        unique_together = ('created_dt', 'device')

DeviceReadings表包含大约2亿行。

如果我这样做,mysql查询将不使用索引,将扫描2200万行,耗时40秒。

#'D1,D2,D3' are comma separated device_uid's
my_devices = "D1,D2,D3".split(",")
devices = Device.objects.filter(device_uid__in=my_devices)
readings = DeviceReadings.objects.filter(created_dt__gte=start_time, created_dt__lte=end_time, device__in=devices)

但是,如果我这样做,mysql查询将使用索引,并且仅扫描100万行,大约需要4秒钟。

my_devices = "D1,D2,D3".split(",")
my_devices_ob = Device.objects.filter(device_uid__in=my_devices)
devices = []
for device in my_devices_ob:
    devices.append(device)
readings = DeviceReadings.objects.filter(created_dt__gte=start_time, created_dt__lte=end_time, device__in=devices)

如果我打印设备数组,则两个代码都相同。有人可以解释一下这里会发生什么吗?

2 个答案:

答案 0 :(得分:0)

请记住,查询集是惰性的。在您的第一个代码中,Device.objects.filter在定义时未执行。由于您是在另一个查询中立即使用它,因此Django会将其转换为以下形式的子查询:

SELECT * FROM device_readings WHERE device_id IN (SELECT id FROM devices WHERE ...);

在第二个查询中,您显式执行第二个查询,因此Django会执行:

SELECT * FROM device_readings WHERE device_id IN ("device_id_1", "device_id_2"...);

通常,第一个查询实际上性能更高,因为您不需要单独获取设备数据。您应该调查一下EXPLAIN为何不是这种情况。

答案 1 :(得分:0)

这两个查询实际上应该具有相同的性能,而第二个查询对于大型表可以忽略不计。因此,您的结果非常不寻常;您可以始终如一地复制它们吗?

我想知道子查询是否使MySQL改变条件的评估顺序,在第二个查询中首先过滤日期。如果向created_dt添加索引可以加快第二个查询的速度,则可能是这样:

created_dt = models.DateTimeField(db_index=True)

我也很好奇以下内容:

my_devices = "D1,D2,D3".split(",")
readings = DeviceReadings.objects.filter(
    created_dt__gte=start_time, 
    created_dt__lte=end_time,
    device__uid__in=my_devices)

它为您提供了更简洁的代码,但可能不会更快。