【问题标题】:How can I make an ORM query that leverages MySQL late row lookups?如何进行利用 MySQL 后期行查找的 ORM 查询?
【发布时间】: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


    【解决方案1】:

    OFFSET 可能导致跳过或重复行。小心。

    OFFSET 是 O(N*N)。这是因为它必须跨过所有“偏移”行。

    更好的解决方案是“记住你离开的地方”,而不是使用OFFSET。见http://mysql.rjweb.org/doc.php/pagination

    你说“现在我被限制/偏移所困”。我不接受。在 ORM 中应该可以重新编写以记住您离开的位置。

    【讨论】:

    • 嘿,非常感谢您的回答!!但不幸的是,这需要通过限制/偏移来解决,因为它听起来很糟糕:(我知道有更好的分页算法,即使是简单的基于光标的分页。“我不接受”,我也不想但我不得不,不幸的是我没有在这个项目中制定规则?
    • 我的博客是基于在 MySQL 4.1 中完成的工作,早在 5.6 之前。也许您的老板应该阅读此问答以及我的博客?
    猜你喜欢
    • 1970-01-01
    • 2018-05-21
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    • 2011-03-11
    • 1970-01-01
    • 2020-08-03
    • 1970-01-01
    相关资源
    最近更新 更多