【问题标题】:Primary key index not getting used主键索引未使用
【发布时间】:2013-11-11 06:38:04
【问题描述】:

我在 postgresql 中创建了一个包含复合主键(3 列)的表。如果在查询中使用不包含前导列的子集,则不会使用默认索引。如果我们显式创建索引,则情况并非如此(索引将用于任何子集)。

默认情况下,postgres 会在主键上创建索引。 但正如postgres document 所说

A multicolumn B-tree index can be used with query conditions that involve any subset ofthe index's columns, but the index is most efficient when there are constraints on the leading (leftmost) columns.

如果查询不包括前导列,那么也会使用索引(如果我们显式创建索引),但是当我们尝试使用默认主键索引的子集时,不会使用索引。

以下是不适用于子集的架构和查询。

# \d client_data
              Table "public.client_data"
       Column       |         Type          | Modifiers 
--------------------+-----------------------+-----------
 macaddr            | character varying(64) | not null
 ts                 | bigint                | not null
 interval           | smallint              | not null
 snr                | smallint              | not null
 rx_rate            | bigint                | 
 tx_rate            | bigint                | 
 rx_data            | bigint                | 
 tx_data            | bigint                | 

Indexes:
    "client_data_pkey" PRIMARY KEY, btree (macaddr, ts, interval)

如果我们指定所有主键列,那么查询规划器将使用索引

# explain analyze select count(*) from client_data where macaddr='a:b:c' and ts=346783556 and interval=5;
                                                              QUERY PLAN                                                              
--------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=8.60..8.61 rows=1 width=0) (actual time=0.040..0.041 rows=1 loops=1)
   ->  Index Scan using client_data_pkey on client_data  (cost=0.00..8.59 rows=1 width=0) (actual time=0.037..0.037 rows=0 loops=1)
         Index Cond: (((macaddr)::text = 'a:b:c'::text) AND (ts = 346783556) AND ("interval" = 5))
 Total runtime: 0.096 ms
(4 rows)

但是如果我们指定子集,查询规划器将不会使用索引

# explain analyze select count(*) from client_data where ts=346783556;
                                                    QUERY PLAN                                                     
-------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=16176.01..16176.02 rows=1 width=0) (actual time=78.937..78.938 rows=1 loops=1)
   ->  Seq Scan on client_data  (cost=0.00..16175.92 rows=36 width=0) (actual time=78.932..78.932 rows=0 loops=1)
         Filter: (ts = 346783556)
 Total runtime: 78.975 ms
(4 rows)


# explain analyze select count(*) from client_data where ts=346783556 and interval=5;
                                                    QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=17639.11..17639.12 rows=1 width=0) (actual time=78.815..78.815 rows=1 loops=1)
   ->  Seq Scan on client_data  (cost=0.00..17639.11 rows=1 width=0) (actual time=78.810..78.810 rows=0 loops=1)
         Filter: ((ts = 346783556) AND ("interval" = 5))
 Total runtime: 78.853 ms
(4 rows)

但如果我们使用带有 ts 或间隔的前导列 (macaddr),则会使用索引。

# explain analyze select count(*) from client_data where macaddr='a' and ts=346783556;
                                                              QUERY PLAN                                                              
--------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=8.59..8.60 rows=1 width=0) (actual time=0.055..0.056 rows=1 loops=1)
   ->  Index Scan using client_data_pkey on client_data  (cost=0.00..8.59 rows=1 width=0) (actual time=0.051..0.051 rows=0 loops=1)
         Index Cond: (((macaddr)::text = 'a'::text) AND (ts = 346783556))
 Total runtime: 0.103 ms
(4 rows)


# explain analyze select count(*) from client_data where macaddr='a' and interval=56;
                                                              QUERY PLAN                                                               
---------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=56.15..56.16 rows=1 width=0) (actual time=0.021..0.022 rows=1 loops=1)
   ->  Index Scan using client_data_pkey on client_data  (cost=0.00..56.15 rows=1 width=0) (actual time=0.017..0.017 rows=0 loops=1)
         Index Cond: (((macaddr)::text = 'a'::text) AND ("interval" = 56))
 Total runtime: 0.055 ms
(4 rows)

【问题讨论】:

  • 您没有向我们展示使用显式创建的索引的示例。

标签: sql postgresql indexing


【解决方案1】:

你应该在你引用的内容之后阅读其余的文字。

PostgreSQL 只能有效地将 b-tree 索引用于包含最左侧列的搜索。您可以将(a,b) 上的索引用于搜索a 的查询或同时查找a 和b 的查询,但不 只搜索b 的查询。这是因为多列 b-tree 索引的结构方式 - 无论如何都必须扫描大部分索引,因此 PostgreSQL 只进行全表扫描通常更有效。

如果您需要将它们作为离散列处理,并且如果您需要在 b 上进行大量搜索/快速搜索,请同时在 b 上创建单独的索引。

您可能会发现,如果您 SET enable_seqscan = off(仅将其用于测试目的)PostgreSQL 会将您的索引用于非最左侧列,但它可能会比 seqscan 慢。如果不是,您需要查看您的random_page_cost 和seq_page_cost 设置是否符合实际情况。

【讨论】:

  • 是的,你是对的,但是当我在 2 个非主列上创建单独的索引时,如果查询仅包含 b,则说(a,b)索引将被使用,唯一的问题是它没有那么多当我包括一个也有效率。查询将使用我们包含 a 或 b 或两者的索引。
  • 很奇怪。可能取决于索引大小等细节。尝试使用规划器成本参数,看看是否只是因为过于昂贵而被避免。
  • 尝试了多个表和不同类型的组合。仅使用默认索引观察到差异。如果多列索引不包含主键,行为是可以解释的,但主键的情况下没有任何线索。
  • @ashu SET enable_seqscan = off 是否强制使用索引?如果有,显示的费用是多少?
  • 是的,设置为关闭,规划器将一直使用索引。但是为什么它们的行为与默认索引和显式创建的索引不同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-22
  • 1970-01-01
  • 2012-04-29
  • 1970-01-01
  • 1970-01-01
  • 2013-08-02
  • 1970-01-01
相关资源
最近更新 更多