我有两段代码,它们都执行相同的操作,但是其中一部代码花费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)
如果我打印设备数组,则两个代码都相同。有人可以解释一下这里会发生什么吗?
答案 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)
它为您提供了更简洁的代码,但可能不会更快。