最近在SO和PostgreSQL邮件列表中都出现了这个问题。
你上述两点的TL;DR:
(a) 更大的shared_buffers可能是CI服务器上TRUNCATE较慢的原因。不同的fsync配置或使用机械硬盘而不是SSD也可能是问题所在。
(b) TRUNCATE
有固定的成本,但不一定比DELETE
慢,而且它做了更多的工作。请参见下面的详细说明。
更新:这篇文章引起了pgsql-performance上的重要讨论。请参阅此线程。
更新2: 9.2beta3中已经添加了改进措施,可以帮助解决这个问题,请参见此帖子。
关于TRUNCATE
与DELETE FROM
的详细解释:
虽然我不是这个主题的专家,但我的理解是,TRUNCATE
每个表的成本几乎是固定的,而DELETE
至少是O(n)(n行); 如果有任何外键引用正在被删除的表,则更糟糕。
我一直认为TRUNCATE
的固定成本比在接近空表上进行DELETE
的成本要低,但事实并非如此。
TRUNCATE table;
比DELETE FROM table;
做得更多。
TRUNCATE table
执行后,数据库的状态与以下命令执行后基本相同:
DELETE FROM table;
VACUUM (FULL, ANALYZE) table;
(仅限9.0+版本,请参见脚注)
... 当然,TRUNCATE
并不是通过DELETE
和VACUUM
来实现其效果。
重点是,DELETE
和TRUNCATE
执行的是不同的操作,因此您不是在比较两个具有相同结果的命令。
DELETE FROM table;
允许死行和膨胀保留,允许索引携带死条目,不更新查询规划器使用的表统计信息等。
TRUNCATE
会给您一个全新的表和索引,就像它们刚刚被创建一样。这就像您删除了所有记录,重新索引了表并进行了VACUUM FULL
。
如果您不在意表格中留下的垃圾,因为您即将再次填充它,那么最好使用
DELETE FROM table;
。由于您没有运行
VACUUM
,您会发现死行和索引条目会积累成膨胀,必须扫描然后忽略;这会减慢所有查询速度。如果您的测试实际上并没有创建和删除太多数据,您可能不会注意到或在意,如果需要,您可以在测试运行过程中进行一两个
VACUUM
。更好的方法是让积极的自动清理设置确保自动清理在后台为您完成。在整个测试套件运行后,仍然可以
TRUNCATE
所有表格,以确保没有影响在多次运行中累积。在9.0及更高版本中,全局对表格进行
VACUUM(FULL, ANALYZE);
至少与
TRUNCATE
同样好,而且更加容易。
我IRC,Pg有一些优化,这意味着它可能会注意到当您的事务是唯一能看到表的事务时,并立即将块标记为自由。在测试中,当我想要创建膨胀时,我必须有多个并发连接来完成。虽然我不会依赖于此。
对于没有外键引用的小表,
DELETE FROM table;
非常便宜。
要从没有外键引用的表中删除所有记录,Pg只需进行顺序表扫描并设置遇到的元组的
xmax
。这是一个非常便宜的操作 - 基本上是线性读取和半线性写入。据我所知,它不必触及索引;它们继续指向死元组,直到稍后的
VACUUM
清理包含仅死元组的块并将其标记为空闲为止。
DELETE
只有在存在大量记录、存在许多必须检查的外键引用或者将
VACUUM (FULL, ANALYZE) table;
的后续操作计入
DELETE
成本时才会变得昂贵。
在我的测试中,
DELETE FROM table;
比
TRUNCATE
快 4 倍,分别为 0.5ms 和 2ms。这是在使用 SSD 的测试数据库上运行的,因为我不在乎数据的丢失,所以设置了
fsync=off
。当然,
DELETE FROM table;
没有执行所有相同的工作,如果我随后执行
VACUUM (FULL, ANALYZE) table;
,则需要更长时间,达到了 21ms,因此仅当我实际上不需要表格原始状态时,
DELETE
才是胜利者。
TRUNCATE table;
执行的固定成本工作和 housekeeping 多于 DELETE
。
相比之下,
TRUNCATE
要做很多工作。它必须为表、其 TOAST 表(如果有)和表拥有的每个索引分配新文件。必须将标题写入这些文件,并且系统目录可能也需要更新(不确定这一点,没有检查)。然后,它必须用新文件替换旧文件或删除旧文件,并确保文件系统已通过同步操作 - fsync() 或类似操作 - 已经跟上更改。我不确定是否在使用(数据错误的)选项
fsync = off
运行时会跳过同步。
我最近了解到,
TRUNCATE
还必须刷新与旧表相关的所有 PostgreSQL 缓冲区。对于巨大的
shared_buffers
,这可能需要相当长的时间。我怀疑这就是为什么在您的 CI 服务器上速度较慢的原因。
无论如何,您可以看到具有关联 TOAST 表(大多数都有)和几个索引的表的
TRUNCATE
可能需要几秒钟。不长,但比从近乎空的表中进行
DELETE
更长。
因此,你最好执行
DELETE FROM table;
。
--
注意:在9.0版本之前的数据库中,
CLUSTER table_id_seq ON table; ANALYZE table;
或者
VACUUM FULL ANALYZE table; REINDEX table;
是与
TRUNCATE
相似的操作。在9.0版本中,
VACUUM FULL
实现方式有了很大的改进。
SERIALIZABLE
事务、DEFERRABLE INITIALLY DEFERRED
约束等,在提交时可能会发生很多事情,所以提交测试更明智。 - Craig Ringer