防止在Postgres中使用特定查询的索引

时间:2016-01-25 13:41:03

标签: sql postgresql indexing query-optimization postgresql-performance

我在Postgres数据库中查询速度慢。使用var result = (from Workflow in db.Workflow join Step in db.Step on Workflow.stepID equals Step.ID join nextStep in db.Step on Workflow.nextStepID equals nextStep.ID select new { nameStep = Step.name, nameNextStep = nextStep.name } ).ToList(); ,我可以看到Postgres在两个不同的索引上进行位图索引扫描,然后在两个结果集上进行位图AND。

删除其中一个索引会使评估速度提高十倍(第一个索引仍然使用位图索引扫描)。但是,删除的索引在其他查询中很有用。

查询:

explain analyze

索引:

select
  booking_id
from
  booking
where
  substitute_confirmation_token is null
  and date_trunc('day', from_time) >= cast('01/25/2016 14:23:00.004' as date)
  and from_time >= '01/25/2016 14:23:00.004'
  and type = 'LESSON_SUBSTITUTE'
  and valid
order by
  booking_id;

查询计划:

"idx_booking_lesson_substitute_day" btree (date_trunc('day'::text, from_time)) WHERE valid AND type::text = 'LESSON_SUBSTITUTE'::text
"booking_substitute_confirmation_token_key" UNIQUE CONSTRAINT, btree (substitute_confirmation_token)

我可以阻止在Postgres中使用特定索引的特定索引吗?

1 个答案:

答案 0 :(得分:0)

你聪明的解决方案

您已经为您的特定情况找到了一个聪明的解决方案:仅包含稀有值的部分唯一索引,因此Postgres不会(不能)将该索引用于公共NULL值。

CREATE UNIQUE INDEX booking_substitute_confirmation_uni
ON booking (substitute_confirmation_token)
WHERE substitute_confirmation_token IS NOT NULL;

这是部分索引的教科书用例。 Literally!本手册有一个类似的例子,这些完美匹配的建议与之相配:

  

最后,部分索引也可用于覆盖系统   查询计划选择。此外,具有特殊分布的数据集可能   当系统真的不应该使用索引时。在那里面   如果索引可以设置,以便它不可用   违规查询。通常,PostgreSQL做出合理的选择   索引使用(例如,它在检索公共值时避免使用它们,所以   前面的例子实际上只保存索引大小,它不是必需的   避免索引使用),并且导致严重错误的计划选择   对于错误报告。

     

请记住,设置部分索引表示您知道   至少与查询规划者知道的一样多,特别是你知道   当一个指数可能有利可图时。形成这种知识需要   体验和理解PostgreSQL中的索引如何工作。在   大多数情况下,部分索引优于常规索引的优势   最小化。

你评论过:The table has few millions of rows and just few thousands of rows with not null values,所以这是一个完美的用例。它甚至可以加快查询substitute_confirmation_token的非空值,因为索引现在 更小。

回答问题

回答原始问题:无法“禁用”特定查询的现有索引。你不得不放弃它,但这是昂贵的。

假下降指数

可以删除事务中的索引,运行SELECT,然后使用ROLLBACK而不是提交。那是,但请注意(per documentation):

  

普通DROP INDEX获取表上的独占锁定,阻塞   其他访问,直到索引丢弃完成。

所以这对多用户环境没有好处。

BEGIN;
DROP INDEX big_user_id_created_at_idx;
SELECT ...;
ROLLBACK;  -- so the index is preserved after all

更详细的统计数据

但是,通常情况下,提升列的STATISTICS目标应该足够了,因此Postgres可以更可靠地识别常用值并避免使用索引。尝试:

ALTER TABLE booking ALTER COLUMN substitute_confirmation_token SET STATISTICS 2000;

然后:ANALYZE booking;再次尝试查询之前。 2000是一个示例值。相关: