2015-05-26 14 views
2

我有一个覆盖索引的表,应该只使用索引来响应查询,根本不检查表。事实上,Postgres确实这样做,如果IN()子句中有1或几个元素。但是,如果IN子句有很多元素,它似乎是在索引上进行搜索,然后进入表格并重新检查条件...为什么PostgreSQL根据其IN()子句的内容不使用* just *覆盖索引?

我不明白为什么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号:

索引扫描使用index_phone_numbers_on_hashed_phone_number上PHONE_NUMBERS(成本= 0.41..8.43行= 1米宽度= 4)
   指数电导率:((hashed_phone_number)::文本= 'bebd43a6eb29b2fda3bcb63dcc7ffaf5433e78660ccd1a495c1180a3eaaf6b6a' ::文本)
   过滤器:(NOT鬼)”

3个数字:

索引只能扫描使用index_phone_numbers_covering_hashed_ghost_and_user上PHONE_NUMBERS(成本= 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(cost = 0.00..926。56行= 106宽度= 0)
               指数电导率:(((hashed_phone_number)::文本= ANY('{b6459ce58f21d99c462b132cce7adc9ea947fa522a3849321e9fb65893006a5e,8228a8116f1fdb12e243102cb85ecd859ebf7873d9332dce5f1343a481ec72e8,ab3554acc1f287bb2e22ff20bb855e19a4177ef552676689d217dbb2a1a6177b,7E(...)

这是目前正在查询,这是查找一个表中有250个记录在总行数为50k的表中,大约是另一个表上的类似查询的两倍,该查询在具有500万行的表中查找250条记录,没什么意义,

任何想法会发生什么,以及我是否可以做任何事情来改善这种情况?


UPDATE:在覆盖索引改变的列的顺序具有第一重影和然后hashed_phone_number也没有解决它:

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

回答

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,那么它就会跳到索引的右边,找到它需要的行。

+0

即使“幽灵”是一个“低选择性”列吗? (大约一半的行是鬼,一半都没有)。我认为最好先设置hashed_phone_number,因为这些选项非常有选择性(它们本质上是一个唯一的键)。我会尝试你的方式,并报告回来。谢谢! –

+0

我刚刚尝试用ghost重新创建索引,并且仍然显示完全相同的行为(检查问题底部的EXPLAIN的新输出)。此外,独立于此,尽管您的解释是有道理的,但我不明白为什么它会进入表格,如果它具有索引中的信息。即使在“坏”情况下,它*是*使用索引... –

+0

@DanielMagliola。 。 。嗯,有趣的是不起作用。我认为,在一个大的'in'条款中,Postgres只是认为第二个执行计划更有效率。 –