reo*_*eox 14 postgresql performance index sorting group-by
我有一个包含 720 万个元组的表,如下所示:
table public.methods
column | type | attributes
--------+-----------------------+----------------------------------------------------
id | integer | not null DEFAULT nextval('methodkey'::regclass)
hash | character varying(32) | not null
string | character varying | not null
method | character varying | not null
file | character varying | not null
type | character varying | not null
Indexes:
"methods_pkey" PRIMARY KEY, btree (id)
"methodhash" btree (hash)
Run Code Online (Sandbox Code Playgroud)
现在我想选择一些值,但查询速度非常慢:
db=# explain
select hash, string, count(method)
from methods
where hash not in
(select hash from nostring)
group by hash, string
order by count(method) desc;
QUERY PLAN
----------------------------------------------------------------------------------------
Sort (cost=160245190041.10..160245190962.07 rows=368391 width=182)
Sort Key: (count(methods.method))
-> GroupAggregate (cost=160245017241.77..160245057764.73 rows=368391 width=182)
-> Sort (cost=160245017241.77..160245026451.53 rows=3683905 width=182)
Sort Key: methods.hash, methods.string
-> Seq Scan on methods (cost=0.00..160243305942.27 rows=3683905 width=182)
Filter: (NOT (SubPlan 1))
SubPlan 1
-> Materialize (cost=0.00..41071.54 rows=970636 width=33)
-> Seq Scan on nostring (cost=0.00..28634.36 rows=970636 width=33)
Run Code Online (Sandbox Code Playgroud)
该hash列是 的 md5 哈希值string并有一个索引。所以我觉得我的问题是整个表是按id而不是hash排序的,所以先排序再分组需要一段时间?
该表nostring仅包含我不想拥有的哈希列表。但是我需要两个表都具有所有值。所以删除这些不是一个选项。
附加信息:没有任何列可以为空(在表定义中修复了该问题)并且我使用的是 postgresql 9.2。
Erw*_*ter 18
将LEFT JOIN在@·德热的答案应该是不错的。然而,索引几乎没有用处(本身),因为无论如何查询都必须读取整个表 - 例外是 Postgres 9.2+ 中的仅索引扫描和有利条件,见下文。
SELECT m.hash, m.string, count(m.method) AS method_ct
FROM methods m
LEFT JOIN nostring n USING (hash)
WHERE n.hash IS NULL
GROUP BY m.hash, m.string
ORDER BY count(m.method) DESC;
Run Code Online (Sandbox Code Playgroud)
运行EXPLAIN ANALYZE查询。多次排除兑现效应和噪音。比较最佳结果。
创建与您的查询匹配的多列索引:
CREATE INDEX methods_cluster_idx ON methods (hash, string, method);
Run Code Online (Sandbox Code Playgroud)
等待?在我说索引没有帮助之后?好吧,我们需要把它CLUSTER放在桌子上:
CLUSTER methods USING methods_cluster_idx;
ANALYZE methods;
Run Code Online (Sandbox Code Playgroud)
重新运行EXPLAIN ANALYZE。有更快的吗?它应该是。
CLUSTER是按使用索引的顺序重写整个表的一次性操作。它也是有效的VACUUM FULL. 如果你想确定,你VACUUM FULL可以单独运行一个预测试,看看可以归因于什么。
如果您的表看到大量写入操作,则效果会随着时间的推移而降低。安排CLUSTER在下班时间以恢复效果。微调取决于您的确切用例。关于CLUSTER.
CLUSTER是一个相当粗糙的工具,需要对表进行排他锁。如果您负担不起,请考虑pg_repack哪个可以在没有排他锁的情况下做同样的事情。更多内容在后面的回答中:
如果NULL列中值的百分比method很高(超过 ~ 20%,取决于实际行大小),部分索引应该有帮助:
CREATE INDEX methods_foo_idx ON methods (hash, string)
WHERE method IS NOT NULL;
Run Code Online (Sandbox Code Playgroud)
(您稍后的更新显示您的列是NOT NULL,因此不适用。)
如果您正在运行 PostgreSQL 9.2或更高版本(如@deszo 评论的那样),CLUSTER如果规划器可以使用仅索引扫描,则所提供的索引可能很有用。仅适用于有利条件:由于VACUUM查询中的最后一列和所有列必须被索引覆盖,因此不会影响可见性映射的写入操作。基本上只读表可以随时使用它,而大量写入的表是有限的。Postgres Wiki 中的更多详细信息。
在这种情况下,上述部分索引可能更有用。
如果,另一方面,列中没有 NULL值method,您应该
1.) 定义它NOT NULL和
2.) 使用count(*)代替count(method),这会稍微快一些,并且在没有NULL值的情况下也是如此。
如果您必须经常调用此查询并且该表是只读的,请创建一个MATERIALIZED VIEW.
异国情调的要点:您的表名为nostring,但似乎包含散列。通过排除散列而不是字符串,您有可能排除比预期更多的字符串。极不可能,但有可能。
欢迎来到 DBA.SE!
您可以尝试像这样重新表述您的查询:
SELECT m.hash, string, count(method)
FROM
methods m
LEFT JOIN nostring n ON m.hash = n.hash
WHERE n.hash IS NULL
GROUP BY hash, string
ORDER BY count(method) DESC;
Run Code Online (Sandbox Code Playgroud)
或另一种可能性:
SELECT m.hash, string, count(method)
FROM
methods m
WHERE NOT EXISTS (SELECT hash FROM nostring WHERE hash = m.hash)
GROUP BY hash, string
ORDER BY count(method) DESC;
Run Code Online (Sandbox Code Playgroud)
NOT IN 是典型的性能接收器,因为它很难与索引一起使用。
这可以通过索引进一步增强。上的索引nostring.hash看起来很有用。但首先:你现在得到了什么?(最好查看 的输出,EXPLAIN ANALYZE因为成本本身并不能说明操作所花费的时间。)