为啥我的 Postgres SQL 查询不使用索引

Posted

技术标签:

【中文标题】为啥我的 Postgres SQL 查询不使用索引【英文标题】:Why doesn't my Postgres SQL Query use an index为什么我的 Postgres SQL 查询不使用索引 【发布时间】:2014-05-03 21:25:57 【问题描述】:

我有一个大约 2300 万行的表 (sales_points)。它在 (store_id, book_id) 上有一个 b-tree 索引。我希望以下查询使用该索引,但 EXPLAIN 表明它正在执行顺序扫描:

select distinct store_id, book_id from sales_points

这是 EXPLAIN 的输出:

Unique  (cost=2050448.88..2086120.31 rows=861604 width=8)
  ->  Sort  (cost=2050448.88..2062339.35 rows=23780957 width=8)
        Sort Key: store_id, book_id
        ->  Seq Scan on sales_points  (cost=0.00..1003261.87 rows=23780957 width=8)

如果我这样做,它确实会使用索引:

select distinct book_id from sales_points where store_id = 1

这是此查询的 EXPLAIN 输出:

HashAggregate  (cost=999671.02..999672.78 rows=587 width=4)
  ->  Bitmap Heap Scan on sales_points  (cost=55576.17..998149.04 rows=3043963 width=4)
        Recheck Cond: (store_id = 1)
        ->  Bitmap Index Scan on index_sales_points_on_store_id_and_book_id  (cost=0.00..55423.97 rows=3043963 width=0)
              Index Cond: (store_id = 1)

这是 DDL 表:

CREATE TABLE sales_points
(
  id serial NOT NULL,
  book_id integer,
  store_id integer,
  date date,
  created_at timestamp without time zone,
  updated_at timestamp without time zone,
  avg_list_price numeric(5,2),
  royalty_amt numeric(9,2),
  currency character varying(255),
  settlement_date date,
  paid_sales integer,
  paid_returns integer,
  free_sales integer,
  free_returns integer,
  lent_units integer,
  lending_revenue numeric(9,2),
  is_placeholder boolean,
  distributor_id integer,
  source1_id integer,
  source2_id integer,
  source3_id integer,
  CONSTRAINT sales_points_pkey PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);

这是索引表达式:

CREATE INDEX index_sales_points_on_store_id_and_book_id
  ON sales_points
  USING btree
  (store_id, book_id);

那么为什么 Postgres 不使用索引来加速 SELECT 呢?

【问题讨论】:

表的定义是什么?你能发布 DDL 吗? 【参考方案1】:

好吧,我认为您的索引工作正常在需要时。您的第一个查询没有 WHERE 子句,因此 Postgres 无论如何都必须检索表中的所有记录。

只是为了测试,你可以通过禁用顺序扫描来强制使用索引:

SET enable_seqscan = OFF;

Postgres 根据各种条件选择它的扫描计划。取自:http://www.postgresql.org/docs/9.2/static/indexes-examine.html

...当不使用索引时,它可以用于强制使用它们的测试。有一些运行时参数可以关闭各种计划类型。例如,关闭顺序扫描 (enable_seqscan) 和嵌套循环连接 (enable_nestloop),这是最基本的计划,将迫使系统使用不同的计划。如果系统仍然选择顺序扫描或嵌套循环连接,那么可能有一个更根本的原因导致没有使用索引...

【讨论】:

第一个查询可以仅通过查看索引来返回,并且 9.2 确实只有索引扫描。所以这个问题是有道理的。 @a_horse_with_no_name 我没有说这不是一个有效的问题;) 我指的是您的说法“Postgres 必须检索表中的所有记录” - 它可能也是索引。

以上是关于为啥我的 Postgres SQL 查询不使用索引的主要内容,如果未能解决你的问题,请参考以下文章

Postgres ltree 不使用 Gist 索引 为啥?

在SQL Server中为啥不建议使用Not In子查询

为啥我的 Sql 查询第二次运行更快?

索引扫描时 Postgres 不使用索引是更好的选择

SQL Server 为啥索引不与 OR 一起使用

为啥Sql Indexed View总是使用聚集索引