我有一个覆盖索引的表,它应该只使用索引来响应查询,而根本不检查表。事实上,如果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(...)
答案 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
,那么它只会跳转到索引的右边部分并找到它所需的行。