Django 不在排序时使用索引

Django does not use index on ordering

我正在尝试优化我在预取中使用的 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 [=32 的输出=] 在生成的 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 树不同的索引吗?还是无法达到我想要的?

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

如果 PostgreSQL 不选择索引是因为它不能或因为它认为排序会更好,要决定这个问题,你可以暂时不鼓励排序:

SET enable_sort = off;

这并没有完全禁用排序,而是对其施加了天文数字的成本惩罚,因此优化器将尽可能选择不同的计划。

然后EXPLAIN再次查询。如果你的索引还没有被使用,它就不能被使用。如果使用索引,则将估计成本与计划成本进行比较并排序。它应该更低。

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