我有一个Postgres 9.4数据库,其中包含如下表格:
| id | other_id | current | dn_ids | rank |
|----|----------|---------|---------------------------------------|------|
| 1 | 5 | F | {123,234,345,456,111,222,333,444,555} | 1 |
| 2 | 7 | F | {123,100,200,900,800,700,600,400,323} | 2 |
(更新)我已经定义了几个索引。这是CREATE TABLE
语法:
CREATE TABLE mytable (
id integer NOT NULL,
other_id integer,
rank integer,
current boolean DEFAULT false,
dn_ids integer[] DEFAULT '{}'::integer[]
);
CREATE SEQUENCE mytable_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
ALTER TABLE ONLY mytable ALTER COLUMN id SET DEFAULT nextval('mytable_id_seq'::regclass);
ALTER TABLE ONLY mytable ADD CONSTRAINT mytable_pkey PRIMARY KEY (id);
CREATE INDEX ind_dn_ids ON mytable USING gin (dn_ids);
CREATE INDEX index_mytable_on_current ON mytable USING btree (current);
CREATE INDEX index_mytable_on_other_id ON mytable USING btree (other_id);
CREATE INDEX index_mytable_on_other_id_and_current ON mytable USING btree (other_id, current);
我需要优化这样的查询:
SELECT id, dn_ids
FROM mytable
WHERE other_id = 5 AND current = F AND NOT (ARRAY[100,200] && dn_ids)
ORDER BY rank ASC
LIMIT 500 OFFSET 1000
此查询工作正常,但我确信智能索引可以更快。表中有大约250,000行,我总是以current = F
作为谓词。我将与存储的数组进行比较的输入数组也将具有1-9个整数。 other_id
可能会有所不同。但通常,在限制之前,扫描将匹配0-25,000行。
以下是EXPLAIN
示例:
Limit (cost=36944.53..36945.78 rows=500 width=65)
-> Sort (cost=36942.03..37007.42 rows=26156 width=65)
Sort Key: rank
-> Seq Scan on mytable (cost=0.00..35431.42 rows=26156 width=65)
Filter: ((NOT current) AND (NOT ('{-1,35257,35314}'::integer[] && dn_ids)) AND (other_id = 193))
此网站上的其他答案和Postgres docs表明可以添加复合索引以提高性能。我已经在[other_id, current]
上有一个。我还在不同的地方读过,除了ORDER BY
子句之外,索引还可以提高WHERE
的性能。
用于此查询的复合索引的正确类型是什么?我根本不在乎空间。
我在WHERE
条款中订购条款的方式是否重要?
答案 0 :(得分:4)
- 用于此查询的正确类型的复合索引是什么?我根本不在乎空间。
醇>
这取决于完整的情况。无论哪种方式,您已经拥有的GIN索引最有可能优于您的GiST索引:
一旦安装了附加模块btree_gin(或btree_gist),您就可以与integer
列合并。
但是,这不包括boolean
数据类型,这通常作为索引列开始没有意义。只有两个(三个包括NULL
)可能的值,它没有足够的选择性。
普通的btree索引对integer
更有效。虽然两个integer
列上的多列btree索引肯定会有所帮助,但您必须仔细测试在多列GIN索引中合并(other_id, dn_ids)
是否比其成本更高。可能不是。 Postgres可以相当有效地组合位图索引扫描中的多个索引。
最后,虽然索引可以用于排序输出,但是这可能不会像你显示那样申请查询(除非你选择表格的大部分)。
不适用于更新的问题。
部分索引可能是一个选项。除此之外,您已经拥有了所需的所有索引。
我会完全删除boolean
列current
上的无意义索引,而rank
上的索引可能永远不会用于此查询。
- 我在
醇>WHERE
条款中订购条款的方式是否重要?
WHERE
条件的顺序完全不相关。
索引的效用绑定到选择性条件。如果选择了超过大约5%(取决于各种因素)的表,则整个表的顺序扫描通常比处理任何索引的开销更快 - 除了预排序输出 ,在这种情况下,索引仍然有用的一件事。
对于获取 25,000个250,000 行的查询,索引通常仅用于此 - 如果您附加 LIMIT
子句,则会更加有趣。一旦满足LIMIT
,Postgres就可以停止从索引中获取行。
请注意,Postgres始终需要阅读OFFSET
+ LIMIT
行,因此性能会随着两者的总和而恶化。
即使您添加了相关信息,相关的大部分内容仍然处于黑暗中。我将假设:
NOT (ARRAY[100,200] && dn_ids)
不 非常有选择性。排除1到10个ID值通常应该保留大部分行,除非dn_ids
中只有很少的不同元素。other_id = 5
。NOT current
删除
旁边:current = F
NOT current
或current = FALSE
; 虽然GIN索引可以很好地识别少数行,其匹配的数组比任何其他索引类型更快,但这似乎与您的查询无关。我最好的猜测是部分,多列btree索引:
CREATE INDEX foo ON mytable (other_id, rank, dn_ids)
WHERE NOT current;
btree索引中的数组列dn_ids
不能支持&&
运算符,我只是包含它以允许index-only scans并在访问堆(表)之前过滤行。如果索引中没有dn_ids
,可能会更快:
CREATE INDEX foo ON mytable (other_id, rank) WHERE NOT current;
GiST索引在Postgres 9.5 due to this new feature中可能会变得更有趣:
允许GiST索引执行仅索引扫描(Anastasia Lubennikova, Heikki Linnakangas,Andreas Karlsson)
除了标准SQL中的current
is a reserved word之外,即使它在Postgres中被允许作为标识符。
除了2:我认为id
是一个实际的serial
列,列默认设置。只是创建一个像你演示的序列,什么都不做。
答案 1 :(得分:2)
不幸的是,我认为你不能将BTree和GIN / GIST索引合并为一个复合索引,因此规划者必须在使用other_id
索引或{{ 1}}索引。正如您所指出的,使用other_id的一个优点是您可以使用多列索引来提高排序性能。你这样做的方式是
dn_ids
这是使用部分索引,并允许您在按排名排序并在other_id上查询时跳过排序步骤。
取决于other_id的基数,唯一的好处可能是排序。因为你的计划有LIMIT条款,所以很难说。如果您使用> SEQ扫描可能是最快的选择。表格的1/5,特别是如果您使用标准硬盘而不是固态硬盘。如果您知道IDX扫描速度更快(您已使用 CREATE INDEX index_mytable_on_other_id_and_current
ON mytable (other_id, rank) WHERE current = F;
进行了测试),那么您的规划师会坚持使用SEQ扫描,您可能需要尝试微调enable_seqscan false
或{{1 }}
最后,我建议不要 保留所有这些索引。找到你需要的,并剔除其余的。索引会导致插入(特别是mutli-column和GIN / GIST索引)性能大幅下降。
答案 2 :(得分:0)
您查询的最简单索引是mytable(other_id, current)
。这处理前两个条件。这将是一个普通的b树类型索引。
您可以使用mytable(dn_ids)
上的GIST索引来满足数组条件。
但是,我认为你不能在一个索引中混合使用不同的数据类型,至少不能没有扩展名。