我的应用中有三个表格,称为tableA
,tableB
和tableC
。 tableA
包含tableB_id
和tableC_id
的字段,两者都有索引。 tableB
的字段foo
包含索引,tableC
的字段bar
包含索引。
当我执行以下查询时:
select *
from tableA
left outer join tableB on tableB.id = tableA.tableB_id
where lower(tableB.foo) = lower(my_input)
它真的很慢(~1秒)。
当我执行以下查询时:
select *
from tableA
left outer join tableC on tableC.id = tabelA.tableC_id
where lower(tableC.bar) = lower(my_input)
它真的很快(约20毫秒)。
据我所知,这些表大小相同。
关于两个查询之间巨大的性能差异的任何想法?
表格大小:
tableA:2061392行
tableB:175339行
tableC:1888912行
Postgres版本 - 9.3.5
查询的全文在上面。
表中的相关信息:
硬件:
OS X 10.10.2
CPU:1.4 GHz Intel Core i5
内存:8 GB 1600 MHz DDR3
图形:Intel HD Graphics 5000 1536 MB
看起来像运行真空,然后分析所有三个表修复了问题。运行命令后,慢速查询开始使用" index_patients_on_foo_tableD"。
答案 0 :(得分:1)
对于初学者,您的LEFT JOIN
会被左表中的谓词抵消,并被迫像[INNER] JOIN
一样行事。替换为:
SELECT *
FROM tableA a
JOIN tableB b ON b.id = a.tableB_id
WHERE lower(b.foo) = lower(my_input);
或者,如果您确实希望LEFT JOIN
包含来自tableA
的所有行:
SELECT *
FROM tableA a
LEFT JOIN tableB b ON b.id = a.tableB_id
AND lower(b.foo) = lower(my_input);
我想你想要第一个。
您发布的 上的索引语法无效。你最好在psql中发布(lower(foo::text))
\d tbl
的逐字输出,就像我反复评论一样。索引定义中强制转换(foo::text
)的简写语法需要更多括号,或使用标准语法:cast(foo AS text)
:
但也是不必要的。您可以使用character varying(255)
的数据类型(foo
)。当然,Postgres中的数据类型character varying(255)
很少有意义。对255个字符的奇怪限制来自其他RDBMS中的限制,这些限制不适用于Postgres。详细说明:
尽管如此。这种查询的完美索引是B
上的多列索引 - 如果(并且仅当)你得到index-only scans:
CREATE INDEX "tableB_lower_foo_id" ON tableB (lower(foo), id);
然后,您可以删除大部分被取代的索引"index_tableB_on_lower_foo"
。 tableC
相同。
其余部分由A
和tableB_id
表tableC_id
中的(更重要!)索引覆盖。
如果tableA
/ tableB_id
/ tableC_id
中有多行,那么这些竞争命令中的任何一个都可以摆动性能以支持相应的查询通过将相关行物理聚类在一起:
CLUSTER tableA USING "index_tableA_on_tableB_id";
CLUSTER tableA USING "index_tableA_on_tableC_id";
你不能兼得。它可以是B
或C
。 CLUSTER
也会执行VACUUM FULL
所做的所有事情。但请务必先阅读详细信息:
不要使用混合大小写标识符,有时引用,有时不引用。这非常令人困惑,必然会导致错误。单独使用合法的小写标识符 - 然后,如果您双重引用它们并不重要。
答案 1 :(得分:1)
另一件事是您将索引列查询为lower()
,这也可以在查询运行时创建部分索引。
如果您始终将该列查询为lower()
,则应将您的列编入索引为lower(column_name)
,如下所示:
create index idx_1 on tableb(lower(foo));
另外,你看过执行计划了吗?如果你能看到它是如何查询表格的话,这将回答你的所有问题。
老实说,这有很多因素。最好的解决方案是研究INDEXES,特别是在Postgres中,这样你就可以看到它们是如何工作的。这是一个整体主题,你可以通过对它们如何工作的最小理解来真正回答你所有的问题。
例如,Postgres有一个初始的"让我们看一下这些表,看看我们应该如何查询它们#34;在查询运行之前。它查看所有表,每个表有多大,存在哪些索引等,然后确定查询应该如何运行。然后它执行它。通常情况下,这是错误的。引擎错误地确定了如何执行它。
很多计算都是在汇总表统计中完成的。您可以通过执行以下操作重置任何表的汇总表统计信息:
vacuum [table_name];
(这有助于防止死行膨胀)
然后:
analyze [table_name];
我一直看不到这项工作,但往往有帮助。
ANyway,最好的选择是:
a)研究Postgres索引(SIMPLE写的,不是一些非常复杂的东西) b)研究查询的执行计划 c)通过了解Postgres索引以及查询计划的执行方式,您无法解决确切的问题。