【问题标题】:Django does not use index on orderingDjango 在排序时不使用索引
【发布时间】:2020-11-27 08:19:50
【问题描述】:

我正在尝试优化我在预取中使用的 Django(版本 1.11)中的查询。所以我有一个与工资相关的 StaffProfile 模型,如下所示:

class Wages(Model):
  # ...
  staff = models.ForeignKey('StaffProfile', on_delete=models.CASCADE, null=True)
  title = models.CharField(_('Title'), max_length=50)


class StaffProfile(Model):
  # ...
  objects = StaffProfileManager.from_queryset(StaffQuerySet)()

StaffQuerySet 有一个专门的方法:

class StaffQuerySet(QuerySet):
  def my_prefetch(
  ) -> QuerySet:
    return self.prefetch_related(
      Prefetch(
        'wages_set',
        queryset=Wages.objects.filter(
          # ...
        ).order_by(
          Upper('title')
        ),
        to_attr='prefetched_wages'
      ),
    )

我注意到当我运行这个查询时:Wages.objects.all().order_by(Upper('title')) 我的数据库(PostgreSQL)显然对结果进行了排序(下面是 EXPLAIN 在生成的 SQL 上运行的输出):

Sort  (cost=21.31..21.86 rows=220 width=166)
  Sort Key: (upper((title)::text))
  ->  Seq Scan on staff_wages  (cost=0.00..12.75 rows=220 width=166)

生成的查询是:

In [25]: str(Wages.objects.all().order_by(Upper('title')).only('title').query)                                                                                                                                                                
Out[25]: 'SELECT "staff_wages"."uuid", "staff_wages"."title" FROM "staff_wages" ORDER BY UPPER("staff_wages"."title") ASC'

所以我尝试在title 字段上设置索引。为了实现这一点,我创建了一个迁移:

class Migration(migrations.Migration):
  dependencies = [
    ('staff', '0020_merge_20201116_0929'),
  ]

  operations = [
    migrations.RunSQL(
      sql='CREATE INDEX "staff_wages_title_upper_idx" ON "staff_wages" (UPPER("title"));',
      reverse_sql='DROP INDEX "staff_wages_title_upper_idx";'
    )
  ]

我已经运行它,我看到它是在数据库中创建的。但是,当我现在运行查询时,EXPLAIN 给出完全相同的输出,而我希望它使用我的索引。我究竟做错了什么?我应该使用与 b-tree 不同的索引吗?还是无法实现我想要的?

【问题讨论】:

  • WHERE 生成的子句.filter(...) 也需要考虑。
  • 生成的SQL语句到底是什么,表是如何定义的。
  • @LaurenzAlbe 我已经用生成的查询更新了我的问题。

标签: django postgresql indexing django-orm


【解决方案1】:

即使你创建了一个索引来支持排序,如果 PostgreSQL 认为排序更便宜,它也可能选择不使用它。

要判断 PostgreSQL 是因为它不能或因为它认为排序会更好而不选择索引,你可以暂时不鼓励排序:

SET enable_sort = off;

这并没有完全禁用排序,但会付出天文数字的代价,因此优化器会尽可能选择不同的计划。

然后EXPLAIN 再次查询。如果您的索引仍未使用,则无法使用。如果使用索引,则将估计的成本与排序的计划成本进行比较。它应该更低。

您可以使用EXPLAIN (ANALYZE) 实际执行查询并显示执行时间以及估计值。这样您就可以检查优化器是否做出了正确的选择。如果不是,并且索引扫描速度更快,则可能是参数 random_page_costeffective_cache_size 未设置为正确描述您的系统。

【讨论】:

  • 好吧,我玩了一会儿,发现它从不使用索引。添加 WHERE 子句后(如问题下的 cmets 中所指出的那样),事实证明,仅索引 WHERE 子句中使用的列时,我获得了最佳性能,并且索引 UPPER(title) 导致性能较差。谢谢你的帮助,它非常有用:-)
猜你喜欢
  • 2012-01-16
  • 2021-11-08
  • 2010-10-18
  • 1970-01-01
  • 2016-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多