【发布时间】:2014-03-28 01:12:33
【问题描述】:
我正在尝试在 PostgreSQL 9.3 中进行一些设置操作。
我有两张桌子,为了简单起见,我们称它们为table_a 和table_b:
create table table_a(id varchar primary key);
create table table_b(id varchar primary key);
我有一个简单的查询(在最简单的表述中,尽管它是实践中插入的来源):
(select id from table_a) except (select id from table_b);
在我开始使用 PostgreSQL 之前,我会做这样的操作:
set-diff table_a.csv table_b.csv > table_c.csv
set-diff 看起来大概像这样:
while (not eof(a)) and (not eof(b)):
line_a <- peek_line(a)
line_b <- peek_line(b)
if line_a < line_b:
output read_line(a)
else if line_a == line_b:
read_line(a)
else:
read_line(b)
while not eof(a):
output read_line(a)
这根本不需要很长时间,内存需求微不足道,并且可以最大限度地有效利用顺序磁盘 I/O。这很重要,因为这台机器没有大量内存 - 它无法将所有数据都放入 RAM。
然而,PostgreSQL 提出了这种计划(来自一些实际的表):
QUERY PLAN
----------------------------------------------------------------------------------
SetOp Except (cost=3184554.28..3238904.44 rows=9434298 width=51)
-> Sort (cost=3184554.28..3211729.36 rows=10870032 width=51)
Sort Key: "*SELECT* 1".id
-> Append (cost=0.00..428039.64 rows=10870032 width=51)
-> Subquery Scan on "*SELECT* 1" (cost=0.00..345707.96 rows=9434298 width=54)
-> Seq Scan on table_a (cost=0.00..251364.98 rows=9434298 width=54)
-> Subquery Scan on "*SELECT* 2" (cost=0.00..82331.68 rows=1435734 width=32)
-> Seq Scan on table_b (cost=0.00..67974.34 rows=1435734 width=32)
查询时间过长 - 几分钟。
我确信 PostgreSQL 可以使用我上面概述的相同类型的合并策略,仅使用索引扫描,而不使用排序。相反,它似乎是连接两个表扫描并对它们进行排序,有点像这个命令行,虽然没有读取 table_b 两次:
sort table_a.csv table_b.csv table_b.csv | uniq -u
这涉及相当多的额外工作 - 例如,当并非所有内容都适合内存时,I/O 的一部分 log(n) 倍。
所涉及的列是 btree 索引的。从查询中选择的唯一列与已编入索引并正在合并的列相同。语言环境到处都是 C。
在我使用大量文本文件和一些自定义索引工具之前。我正在尝试使用数据库来获得额外的查询灵活性并避免维护自定义索引。然而性能令人震惊,以至于我正在考虑在数据库之外进行合并和大多数其他大规模更新操作,通过 csv 来回传输数据。
我错过了什么?
【问题讨论】:
-
您可能希望将此问题迁移到 dba.stackexchange.com。
标签: sql database performance postgresql