【发布时间】:2020-01-20 00:27:45
【问题描述】:
在我的 django 应用程序中,我定义了一个这样的模型:
class NamedContainer(models.Model):
name = models.CharField(max_length=50)
capacity_ml = models.PositiveIntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['name', 'capacity_ml']
随着时间的推移,这个表已经变得足够大,开始导致一些查询性能问题,特别是在依赖限制/偏移进行切片时。其他切片方法在设计上可能具有更好的性能,但不幸的是,现在我被限制/偏移所困扰。
但是,MySQL has a technique called "late row lookups" 对我的问题有很大帮助,简而言之,使用这种技术的原始 MySQL 查询可能看起来像
SELECT t2.* FROM (
SELECT *
FROM `core_namedcontainer`
WHERE `updated_at` >= '2019-01-01 05:00:00.000Z'
ORDER BY id ASC
LIMIT 500 OFFSET 10000
) AS t1
JOIN `core_namedcontainer` AS t2
ON t1.id = t2.id
ORDER BY `name` ASC, `capacity_ml` ASC
我只设法破坏 ORM 查询以产生类似的查询
SELECT *
FROM `core_namedcontainer`
WHERE (
`core_namedcontainer`.`id` IN (
SELECT U0.`id`
FROM `core_namedcontainer` U0
WHERE (
U0.`updated_at` >= 2019-01-01 05:00:00
)
ORDER BY U0.`name` ASC, U0.`capacity_ml` ASC
LIMIT 500 OFFSET 10000
)
)
ORDER BY `core_namedcontainer`.`name` ASC, `core_namedcontainer`.`capacity_mL` ASC
它只是使用子查询而不是自身的连接,并且在评估查询时,MySQL 会抱怨
NotSupportedError: (1235, "This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'")
django 应用使用 MySQL 5.6,由于各种原因,它需要很长时间才能升级到任何更新版本。
我知道我可以按照我需要的方式发出原始 SQL 查询,但我希望有一种方法可以将这种技术转换为 Django ORM 语法,以便我可以在我的模型上利用这种技术作为基础查询集:
class NamedContainerManager(models.Manager):
def get_queryset(self):
queryset = super().get_queryset()
# do some ORM magic here to implement mysql late row lookup in all queries
return queryset
class NamedContainer(models.Model):
...
objects = NamedContainerManager()
非常感谢所有帮助!
【问题讨论】:
标签: mysql django django-models django-orm database-performance