未使用smallint []列的GIN索引或错误“运算符不唯一”

时间:2016-09-30 19:41:55

标签: performance postgresql indexing operator-overloading postgresql-9.5

create table test(
    id serial primary key,
    tagged smallint[]
);

tagged列上有杜松子酒索引,_int2_ops运算符类:

CREATE INDEX ix ON test USING GIN(col _int2_ops);

当我运行此查询时:

select * from test
where tagged @> ARRAY[11]
order by id limit 100;

EXPLAIN ANALYZE显示:

Limit  (cost=0.43..19524.39 rows=100 width=36) (actual time=25024.124..25027.263 rows=100 loops=1)
  ->  Index Scan using test_pkey on test  (cost=0.43..508404.37 rows=2604 width=36) (actual time=25024.121..25027.251 rows=100 loops=1)
        Filter: ((tagged)::integer[] @> '{11}'::integer[])
        Rows Removed by Filter: 2399999
Planning time: 6.912 ms
Execution time: 25027.307 ms

大胆强调我的。为什么tagged列转换为integer[]类型?我认为这就是为什么不使用GIN索引并且查询运行缓慢的原因。

我尝试了WHERE tagged @> ARRAY[11]::smallint[],但收到了这个错误:

operator is not unique: smallint[] @> smallint[]

如果我这样做但使用tagged int[]并将索引创建为

CREATE INDEX ix ON test USING GIN(tagged gin__int_ops);

然后上面的查询使用GIN索引:

"->  Bitmap Index Scan on ix  (cost=0.00..1575.53 rows=2604 width=0) (actual time=382.840..382.840 rows=2604480 loops=1)"
"   Index Cond: (tagged @> '{11}'::integer[])"

这比以前快一点,但平均需要10秒 - 仍然太慢。我想尝试smallint[]代替int[],也许会更快......

1 个答案:

答案 0 :(得分:1)

解决方案

最有可能的是,解决方案是对运营商进行架构限定:

SELECT *
FROM   test
WHERE  tagged OPERATOR(pg_catalog.@>) '{11}'::int2[]
ORDER  BY id
LIMIT  100;

为什么?

这是操作员解析的问题(与类型解析和投射上下文相结合)。

在标准的Postgres中,只有一个候选运算符anyarray @> anyarray,就是你想要的那个。

如果您没有安装附加模块intarray (我的假设),那么您的设置就可以正常工作,这为integer[] @> integer[]提供了另一个操作员。

因此,另一种解决方案是使用integer[]代替并使用gin__int_ops operator class的GIN索引。或者尝试(默认为intarray)gist__int_ops索引。要么可能更快,但两者都不允许NULL值 或者,您可以重命名intarray运算符@>以消除歧义。 (我不会这样做。随后会出现升级和可移植性问题。)

对于涉及至少一个类型为integer[]的操作数的表达式,Postgres知道要选择哪个运算符:intarray运算符。但是 索引不适用 ,因为intarray运算符仅对integerint4)而不是int2进行操作。索引严格限制在运营商身上:

但对于int2[] @> int2[],Postgres无法决定最佳运营商。两者似乎同样适用。由于默认运算符在pg_catalog模式中提供,并且intarray运算符在public模式中提供(默认情况下 - 或者在您安装扩展的任何位置),您可以通过模式限定来帮助解决难题OPERATOR() construct的运算符。相关:

您收到的错误消息有点误导。但如果仔细观察,会添加HINT行,提示(tada!)方向正确:

ERROR:  operator is not unique: smallint[] @> smallint[]
LINE 1: SELECT NULL::int2[] @> NULL::int2[]
                            ^
HINT:  Could not choose a best candidate operator. You might need to add explicit type casts.

您可以使用以下内容调查@>的现有运营商候选人:

SELECT o.oid, *, oprleft::regtype, oprright::regtype, n.nspname
FROM   pg_operator o
JOIN   pg_namespace n ON n.oid = o.oprnamespace
WHERE  oprname = '@>';

另一种替代解决方案是临时(!)设置不同的search_path,因此只找到所需的运算符。在同一笔交易中:

SET LOCAL search_path = pg_catalog;
SELECT ...

但是你必须对查询中的所有表进行模式限定。

关于演员表:

可以更改castcontext的{​​{1}} - > int2。但我强烈建议不要这样做。太多可能的副作用: