我有一个带有覆盖索引的表,它应该只使用索引来响应查询,而不需要检查表。事实上,如果 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个数字:
Index Only Scan using index_phone_numbers_covering_hashed_ghost_and_user on phone_numbers (cost=0.42..17.29 rows=1 width=4)
Index Cond: ((hashed_phone_number = ANY ('{8228a8116f1fdb12e243102cb85ecd859ebf7873d9332dce5f1343a481ec72e8,43ddeebdca2ea829d468d5debc84d475c8322cf4bf6edca286c918b04216387e,1578bf773eb6eb8a9b57a130922a28c9c91f1bda67202ef5936b39630ca4cfe4}'::text[])) AND (.. .)
过滤器:(不是幽灵)”
但是,当我在 IN 子句中有很多数字时,Postgres 正在使用索引,但随后击中表,我不知道为什么:
Bitmap Heap Scan on phone_numbers (cost=926.59..1255.81 rows=106 width=4) Recheck
Cond: ((hashed_phone_number)::text = ANY ('{b6459ce58f21d99c462b132cce7adc9ea947fa522a3849321e9fb65893006a5e,8228a8116f1fdb12e243102cb85ecd859ebf7873d9332dce5f1343a481ec72e8,ab3554acc1f287bb2e22ff20bb855e19a4177ef552676689d217dbb2a1a6177b,7ec9f58 (...)
Filter: (NOT ghost)
-> Bitmap Index Scan on index_phone_numbers_covering_hashed_ghost_and_user (cost=0.00..926.56 rows=106 width=0)
Index Cond: (((hashed_phone_number)::text = ANY ('{b6459ce58f21d99c462b132cce7adc9ea947fa522a3849321e9fb65893006a5e,8228a8116f1fdb12e243102cb85ecd859ebf7873d9332dce5f1343a481ec72e8,ab3554acc1f287bb2e22ff20bb855e19a4177ef552676689d217dbb2a1a6177b,7e (... )
当前正在执行此查询,该查询在总行数为 50k 的表中查找 250 条记录,大约是在另一个表上查找类似查询的两倍,后者在具有 500 万行的表中查找 250 条记录,而不是很有意义。
任何想法可能会发生什么,以及我是否可以做任何事情来改善这一点?
更新:将覆盖索引中的列顺序更改为先显示重影,然后再使用 hashed_phone_number 也不能解决问题:
Bitmap Heap Scan on phone_numbers (cost=926.59..1255.81 rows=106 width=4) Recheck
Cond: ((hashed_phone_number)::text = ANY ('{b6459ce58f21d99c462b132cce7adc9ea947fa522a3849321e9fb65893006a5e,8228a8116f1fdb12e243102cb85ecd859ebf7873d9332dce5f1343a481ec72e8,ab3554acc1f287bb2e22ff20bb855e19a4177ef552676689d217dbb2a1a6177b,7ec9f58 (...)
Filter: (NOT ghost)
-> Bitmap Index Scan on index_phone_numbers_covering_ghost_hashed_and_user (cost=0.00..926.56 rows=106 width=0)
Index Cond: ((ghost = false) AND ((hashed_phone_number)::text = ANY ('{b6459ce58f21d99c462b132cce7adc9ea947fa522a3849321e9fb65893006a5e,8228a8116f1fdb12e243102cb85ecd859ebf7873d9332dce5f1343a481ec72e8,ab3554acc1f287bb2e22ff20bb855e19a4177ef55267668 (...)