Django ORM 对 QuerySet 的迭代真的很慢
Django ORM really slow iterating over QuerySet
我正在做一个新项目,必须非常快速地构建几页的大纲。
我导入了一个包含 280,000 个产品的目录,我想进行搜索。我选择了 Whoosh 和 Haystack 来提供搜索,因为我在之前的项目中使用过它们。
我为索引添加了定义并启动了该过程。然而,Django 似乎真的非常非常真的 遍历 QuerySet 很慢。
最初,我认为索引编制花费了超过 24 小时——这看起来很荒谬,所以我测试了一些其他的东西。我现在可以确认迭代 QuerySet 需要很多小时。
也许在 Django 2.2 中有一些我不习惯的地方?我以前使用的是 1.11,但我想我现在使用的是更新的版本。
我要迭代的模型:
class SupplierSkus(models.Model):
sku = models.CharField(max_length=20)
link = models.CharField(max_length=4096)
price = models.FloatField()
last_updated = models.DateTimeField("Date Updated", null=True, auto_now=True)
status = models.ForeignKey(Status, on_delete=models.PROTECT, default=1)
category = models.CharField(max_length=1024)
family = models.CharField(max_length=20)
family_desc = models.TextField(null=True)
family_name = models.CharField(max_length=250)
product_name = models.CharField(max_length=250)
was_price = models.FloatField(null=True)
vat_rate = models.FloatField(null=True)
lead_from = models.IntegerField(null=True)
lead_to = models.IntegerField(null=True)
deliv_cost = models.FloatField(null=True)
prod_desc = models.TextField(null=True)
attributes = models.TextField(null=True)
brand = models.TextField(null=True)
mpn = models.CharField(max_length=50, null=True)
ean = models.CharField(max_length=15, null=True)
supplier = models.ForeignKey(Suppliers, on_delete=models.PROTECT)
而且,正如我提到的,table.
中大约有 280k 行
当我做一些简单的事情时:
from products.models import SupplierSkus
sku_list = SupplierSkus.objects.all()
len(sku_list)
该进程将很快耗尽大部分 CPU 电量并且不会完成。同样,我不能迭代它:
for i in sku_list:
print(i.sku)
也只需要几个小时而不打印一行。但是,我可以使用以下方法对其进行迭代:
for i in sku_list.iterator():
print(i.sku)
这对我帮助不大,因为我仍然需要通过 Haystack 进行索引,我相信这些问题是相关的。
我以前参与的一些项目并非如此。即使是更大的列表(3-5m 行)也会很快迭代。查询列表长度需要一些时间,但 return 结果以秒而不是小时为单位。
所以,我想知道,这是怎么回事?
这是其他人遇到的事情吗?
好的,我发现问题出在 Python MySQL 驱动程序上。如果不使用 .iterator()
方法,for
循环将卡在 QuerySet 的最后一个元素上。我已经在 上发布了更详细的答案。
I was not using the Django recommended mysqlclient. I was using the
one created by Oracle/MySQL. There seems to be a bug that causes an
iterator to get "stuck" on the last element of the QuerySet in a for
loop and be trapped in an endless loop in certain circumstances.
仔细想想,这很可能是MySQL驱动的一个设计特点。我记得以前使用此驱动程序的 Java 版本时遇到过类似的问题。也许我应该放弃 MySQL 并转向 PostgreSQL?
无论如何我都会尝试用 Oracle 提出错误。
我正在做一个新项目,必须非常快速地构建几页的大纲。
我导入了一个包含 280,000 个产品的目录,我想进行搜索。我选择了 Whoosh 和 Haystack 来提供搜索,因为我在之前的项目中使用过它们。 我为索引添加了定义并启动了该过程。然而,Django 似乎真的非常非常真的 遍历 QuerySet 很慢。 最初,我认为索引编制花费了超过 24 小时——这看起来很荒谬,所以我测试了一些其他的东西。我现在可以确认迭代 QuerySet 需要很多小时。
也许在 Django 2.2 中有一些我不习惯的地方?我以前使用的是 1.11,但我想我现在使用的是更新的版本。
我要迭代的模型:
class SupplierSkus(models.Model):
sku = models.CharField(max_length=20)
link = models.CharField(max_length=4096)
price = models.FloatField()
last_updated = models.DateTimeField("Date Updated", null=True, auto_now=True)
status = models.ForeignKey(Status, on_delete=models.PROTECT, default=1)
category = models.CharField(max_length=1024)
family = models.CharField(max_length=20)
family_desc = models.TextField(null=True)
family_name = models.CharField(max_length=250)
product_name = models.CharField(max_length=250)
was_price = models.FloatField(null=True)
vat_rate = models.FloatField(null=True)
lead_from = models.IntegerField(null=True)
lead_to = models.IntegerField(null=True)
deliv_cost = models.FloatField(null=True)
prod_desc = models.TextField(null=True)
attributes = models.TextField(null=True)
brand = models.TextField(null=True)
mpn = models.CharField(max_length=50, null=True)
ean = models.CharField(max_length=15, null=True)
supplier = models.ForeignKey(Suppliers, on_delete=models.PROTECT)
而且,正如我提到的,table.
中大约有 280k 行当我做一些简单的事情时:
from products.models import SupplierSkus
sku_list = SupplierSkus.objects.all()
len(sku_list)
该进程将很快耗尽大部分 CPU 电量并且不会完成。同样,我不能迭代它:
for i in sku_list:
print(i.sku)
也只需要几个小时而不打印一行。但是,我可以使用以下方法对其进行迭代:
for i in sku_list.iterator():
print(i.sku)
这对我帮助不大,因为我仍然需要通过 Haystack 进行索引,我相信这些问题是相关的。
我以前参与的一些项目并非如此。即使是更大的列表(3-5m 行)也会很快迭代。查询列表长度需要一些时间,但 return 结果以秒而不是小时为单位。
所以,我想知道,这是怎么回事? 这是其他人遇到的事情吗?
好的,我发现问题出在 Python MySQL 驱动程序上。如果不使用 .iterator()
方法,for
循环将卡在 QuerySet 的最后一个元素上。我已经在
I was not using the Django recommended mysqlclient. I was using the one created by Oracle/MySQL. There seems to be a bug that causes an iterator to get "stuck" on the last element of the QuerySet in a for loop and be trapped in an endless loop in certain circumstances.
仔细想想,这很可能是MySQL驱动的一个设计特点。我记得以前使用此驱动程序的 Java 版本时遇到过类似的问题。也许我应该放弃 MySQL 并转向 PostgreSQL?
无论如何我都会尝试用 Oracle 提出错误。