为什么PostgreSQL在这个查询中不使用*just*覆盖索引,这取决于它的in()子句的内容?

为什么PostgreSQL在这个查询中不使用*just*覆盖索引,这取决于它的in()子句的内容?,sql,postgresql,indexing,query-optimization,covering-index,Sql,Postgresql,Indexing,Query Optimization,Covering Index,我有一个带有覆盖索引的表,它应该只使用索引响应查询,而根本不检查表。实际上,如果in()子句中有1个或几个元素,Postgres就会这样做。但是,如果IN子句有很多元素,它看起来就像是在索引上搜索,然后转到表并重新检查条件 我不明白为什么博士后会这么做。它可以直接从索引提供查询,也可以不提供查询,如果它(理论上)没有任何其他要添加的内容,为什么要转到表中 下表: CREATE TABLE phone_numbers ( id serial NOT NULL, phone_number c

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

我不明白为什么博士后会这么做。它可以直接从索引提供查询,也可以不提供查询,如果它(理论上)没有任何其他要添加的内容,为什么要转到表中

下表:

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号:

使用索引电话号码对电话号码进行索引扫描(成本=0.41..8.43行=1宽度=4)
索引条件:((哈希电话号码)::text='bebd43a6eb29b2fda3bcb63dcc7ffaf5433e78660ccd1a495c1180a3eaaf6b6a'::text)
过滤器:(不是幽灵)

3个数字:

仅使用索引电话号码和用户电话号码(成本=0.42..17.29行=1宽度=4)进行索引扫描
索引条件:((哈希电话号码=任意({8228A8116F1FDB12E243102CB85ECD859EBF773D9332DCE5F1343A481EC72E8,43DDEDCA2EA829D468D5DEBC84D475C8322CF4BF6EDCA286C918B0421638E,1578BF773EB6EB8A9B57A13092A28C9C91BDA67202EF539630CA4CFE4}):文本[])和(
过滤器:(不是幽灵)

然而,当我在in子句中有很多数字时,Postgres会使用索引,但随后会碰到表,我不知道为什么:

电话号码上的位图堆扫描(成本=926.59..1255.81行=106宽度=4)
重新检查条件:((哈希电话号码)::text=ANY({b6459ce58f21d99c462b132cce7adc9ea947fa522a3849321e9fb65893006a5e,8228a8116f1fdb12e243102cb85ecd859ebf7873d9332dce5343a481ec72e8,ab3554acc1f287bb2f20bb855e19a4177ef5576689d17b2a117b,7ec9f58(…)
过滤器:(非重影)
->对索引电话号码和用户进行位图索引扫描(成本=0.00..926.56行=106宽度=0)
索引条件:((哈希电话号码)::text=ANY({b6459ce58f21d99c462b132cce7adc9ea947fa522a3849321e9fb65893006a5e,8228a8116f1fdb12e2243102cb85ecd859ebf7873d9332dce53a343a481ec72e8,ab3554acc1f287b22fe2f20bb855e19a4177ef5576689d17b2a117b,7e(…)

目前正在进行此查询,它在一个总共有50k行的表中查找250条记录,大约是另一个表中类似查询的两倍,而另一个表在一个有500万行的表中查找250条记录,这没有多大意义

有什么想法会发生什么,我是否可以做些什么来改善这一点


更新:将覆盖索引中列的顺序更改为先有重影,然后再散列电话号码也无法解决此问题:

电话号码上的位图堆扫描(成本=926.59..1255.81行=106宽度=4)
重新检查条件:((哈希电话号码)::text=ANY({b6459ce58f21d99c462b132cce7adc9ea947fa522a3849321e9fb65893006a5e,8228a8116f1fdb12e243102cb85ecd859ebf7873d9332dce5343a481ec72e8,ab3554acc1f287bb2f20bb855e19a4177ef5576689d17b2a117b,7ec9f58(…)
过滤器:(非重影)
->在索引\u电话\u号码\u覆盖\u重影\u散列\u和用户上进行位图索引扫描(成本=0.00..926.56行=106宽度=0)
索引条件:((ghost=false)和((哈希电话号码)::text=ANY({b6459ce58f21d99c462b132cce7adc9ea947fa522a3849321e9fb65893006a5e,8228a8116f1fdb12e243102cb85ecd859ebf7873d9332dce53f1343a481ec72e8,ab3554acc1f287bb2f20bb855e19a4177ef55267668(…)


索引的选择取决于优化器所说的是查询的最佳解决方案。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在解释复合索引的使用方面做得很好


本质上,Postgres需要对列表中
的每个元素进行索引查找。这可能会因为使用字符串而变得复杂,因为排序/编码会影响比较。最终,Postgres会决定其他方法更有效。如果你先放置
ghost
,那么它会更有效只需跳转到索引的右侧部分,并在那里找到它需要的行。

即使“ghost”是一个“低选择性”列(大约一半的行是ghost,一半不是)。我认为最好先对电话号码进行散列,因为它们具有极大的选择性(它们本质上是唯一的键)。我会试试你的方法,然后再报告。谢谢!我刚刚尝试先用ghost重新创建索引,但它仍然表现出完全相同的行为(检查问题底部解释的新输出)此外,与此无关,虽然你的解释有道理,但我不明白如果索引中有信息,为什么它会出现在表中。甚至在“坏”中在某些情况下,它使用的是索引…@DanielMagliola…嗯,有趣的是它不起作用。我想,在
子句中有一个大的
,Postgres只是认为第二个执行计划更有效。