为什么PostgreSQL在此查询中不使用* just *覆盖索引,具体取决于其IN()子句的内容?

时间:2015-05-26 11:40:42

标签: sql postgresql indexing query-optimization covering-index

我有一个覆盖索引的表,它应该只使用索引来响应查询,而根本不检查表。事实上,如果IN()子句中包含1个或几个元素,Postgres就会这样做。但是,如果IN子句有很多元素,看起来它正在对索引进行搜索,然后转到表并重新检查条件......

我无法弄清楚为什么Postgres会这样做。它既可以直接从索引提供查询,也可以不提供,如果它(理论上)没有其他东西可以添加,为什么会进入表格呢?

表格:

CREATE TABLE phone_numbers
(
  id serial NOT NULL,
  phone_number character varying,
  hashed_phone_number character varying,
  user_id integer,
  created_at timestamp without time zone,
  updated_at timestamp without time zone,
  ghost boolean DEFAULT false,
  CONSTRAINT phone_numbers_pkey PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);

CREATE INDEX index_phone_numbers_covering_hashed_ghost_and_user
  ON phone_numbers
  USING btree
  (hashed_phone_number COLLATE pg_catalog."default", ghost, user_id);

我正在运行的查询是:

SELECT "phone_numbers"."user_id" 
FROM "phone_numbers" 
WHERE "phone_numbers"."hashed_phone_number" IN (*several numbers*) 
  AND "phone_numbers"."ghost" = 'f'

如您所见,索引具有回复该查询所需的所有字段。

如果我在IN子句中只有一个或几个数字,它会:

1号:

  

使用phone_numbers上的index_phone_numbers_on_hashed_phone_number进行索引扫描(成本= 0.41..8.43行= 1宽度= 4)
  索引条件:((hashed_phone_number):: text ='bebd43a6eb29b2fda3bcb63dcc7ffaf5433e78660ccd1a495c1180a3eaaf6b6a':: text)
  过滤:(不是幽灵)“

3个数字:

  

仅索引使用phone_numbers上的index_phone_numbers_covering_hashed_ghost_and_user扫描(费用= 0.42..17.29行= 1宽度= 4)
  指数电导率:((hashed_phone_number = ANY( '{8228a8116f1fdb12e243102cb85ecd859ebf7873d9332dce5f1343a481ec72e8,43ddeebdca2ea829d468d5debc84d475c8322cf4bf6edca286c918b04216387e,1578bf773eb6eb8a9b57a130922a28c9c91f1bda67202ef5936b39630ca4cfe4}' ::文[]))AND(...)
  过滤:(不是幽灵)“

然而,当我在IN子句中有很多数字时,Postgres正在使用索引,但随后点击表格,我不知道为什么:

  

phone_numbers上的位图堆扫描(成本= 926.59..1255.81行= 106宽度= 4)
  重新检查电导率:((hashed_phone_number)::文本= ANY('{b6459ce58f21d99c462b132cce7adc9ea947fa522a3849321e9fb65893006a5e,8228a8116f1fdb12e243102cb85ecd859ebf7873d9332dce5f1343a481ec72e8,ab3554acc1f287bb2e22ff20bb855e19a4177ef552676689d217dbb2a1a6177b,7ec9f58(...)
  过滤:(不是鬼)
   - > index_phone_numbers_covering_hashed_ghost_and_user上的位图索引扫描(成本= 0.00..926.56行= 106宽度= 0)
  指数电导率:(((hashed_phone_number)::文本= ANY('{b6459ce58f21d99c462b132cce7adc9ea947fa522a3849321e9fb65893006a5e,8228a8116f1fdb12e243102cb85ecd859ebf7873d9332dce5f1343a481ec72e8,ab3554acc1f287bb2e22ff20bb855e19a4177ef552676689d217dbb2a1a6177b,7E(...)

这正在进行此查询,它在一个总行数为50k的表中查找250条记录,大约是另一个表上类似查询的两倍,它在一个包含500万行的表中查找250条记录,没有多大意义。

任何想法可能会发生什么,以及我是否可以采取任何措施来改善这一点?

UPDATE :更改覆盖索引中列的顺序为第一个鬼,然后是hashed_phone_number也无法解决:

  

phone_numbers上的位图堆扫描(成本= 926.59..1255.81行= 106宽度= 4)
  重新检查电导率:((hashed_phone_number)::文本= ANY('{b6459ce58f21d99c462b132cce7adc9ea947fa522a3849321e9fb65893006a5e,8228a8116f1fdb12e243102cb85ecd859ebf7873d9332dce5f1343a481ec72e8,ab3554acc1f287bb2e22ff20bb855e19a4177ef552676689d217dbb2a1a6177b,7ec9f58(...)
  过滤:(不是鬼)
   - > index_phone_numbers_covering_ghost_hashed_and_user上的位图索引扫描(成本= 0.00..926.56行= 106宽度= 0)
  指数电导率:((鬼=假)AND((hashed_phone_number)::文本= ANY('{b6459ce58f21d99c462b132cce7adc9ea947fa522a3849321e9fb65893006a5e,8228a8116f1fdb12e243102cb85ecd859ebf7873d9332dce5f1343a481ec72e8,ab3554acc1f287bb2e22ff20bb855e19a4177ef55267668(...)

1 个答案:

答案 0 :(得分:0)

索引的选择基于优化程序所说的查询的最佳解决方案。 Postgres正在尝试使用您的索引,但它不是查询的最佳索引。

最佳索引首先是ghost

CREATE INDEX index_phone_numbers_covering_hashed_ghost_and_user
  ON phone_numbers
  USING btree
  (ghost, hashed_phone_number COLLATE pg_catalog."default", user_id);

我碰巧认为MySQL documentation在解释如何使用复合索引方面做得很好。

基本上,正在发生的事情是Postgres需要对in列表的每个元素进行索引搜索。使用字符串可能会加剧这种情况 - 因为整理/编码会影响比较。最终,Postgres决定其他方法更有效。如果你先放ghost,那么它只会跳转到索引的右边部分并找到它所需的行。