这在 SO 和 PostgreSQL 邮件列表上都出现了几次。
最后两点的TL;DR :
(a) 较大的 shared_buffers 可能是 CI 服务器上 TRUNCATE 较慢的原因。不同的 fsync 配置或使用旋转介质而不是 SSD 也可能有问题。
(b)TRUNCATE
具有固定成本,但不一定比 慢,而且DELETE
它做更多的工作。请参阅下面的详细说明。
更新:这篇文章引起了关于 pgsql 性能的重要讨论。看到这个线程。
更新 2: 9.2beta3 中添加了改进,应该对此有所帮助,请参阅这篇文章。
TRUNCATE
vs的详细解释DELETE FROM
:
虽然不是该主题的专家,但我的理解是TRUNCATE
每张表的成本几乎是固定的,而DELETE
对于 n 行至少是 O(n);如果有任何外键引用正在删除的表,则更糟。
我一直认为 a 的固定成本TRUNCATE
低于DELETE
在几乎空的桌子上 a 的成本,但事实并非如此。
TRUNCATE table;
超过DELETE FROM table;
a 之后的数据库状态与TRUNCATE table
您改为运行时大致相同:
DELETE FROM table;
VACCUUM (FULL, ANALYZE) table;
(仅限 9.0+,见脚注)
...虽然当然TRUNCATE
实际上并没有通过 aDELETE
和 a实现其效果VACUUM
。
关键是做不同的事情DELETE
,TRUNCATE
所以你不只是比较两个具有相同结果的命令。
ADELETE FROM table;
允许保留死行和膨胀,允许索引携带死条目,不更新查询计划器使用的表统计信息等。
ATRUNCATE
为您提供了一个全新的表和索引,就好像它们刚刚被CREATE
编辑过一样。就像您删除了所有记录,重新索引表并执行了VACUUM FULL
.
如果您不关心表中是否还有残留物,因为您将要再次将其填满,那么您最好使用DELETE FROM table;
.
因为您没有运行VACUUM
,您会发现死行和索引条目累积为必须扫描然后忽略的膨胀;这会减慢您的所有查询速度。如果您的测试实际上并没有创建和删除您可能不会注意到或关心的所有数据,那么VACUUM
如果您这样做了,您总是可以在测试运行的中途进行一两次。更好的是,让积极的 autovacuum 设置确保 autovacuum 在后台为您执行此操作。
在整个测试套件运行后,您仍然可以TRUNCATE
使用所有表,以确保在多次运行中不会产生任何影响。在 9.0 和更高版本上,全局在桌面上至少是一样好,如果不是更好的话,而且要容易得多。VACUUM (FULL, ANALYZE);
IIRC Pg 有一些优化,这意味着它可能会注意到您的事务是唯一可以看到表并立即将块标记为空闲的事务。在测试中,当我想创建膨胀时,我必须有多个并发连接才能做到这一点。不过,我不会依赖这个。
DELETE FROM table;
对于没有 f/k refs 的小桌子来说非常便宜
对于DELETE
没有外键引用的表中的所有记录,所有 Pg 都必须执行顺序表扫描并设置xmax
遇到的元组。这是一个非常便宜的操作——基本上是线性读取和半线性写入。AFAIK 它不必触及索引;它们继续指向死元组,直到它们被稍后清理,VACUUM
这也将表中仅包含死元组的块标记为空闲。
DELETE
如果有很多记录,如果有很多必须检查的外键引用,或者如果VACUUM (FULL, ANALYZE) table;
您TRUNCATE
在DELETE
.
在我在这里的测试中,a通常比0.5ms 和 2msDELETE FROM table;
快 4 倍。TRUNCATE
那是 SSD 上的测试数据库,正在运行,fsync=off
因为我不在乎我是否会丢失所有这些数据。当然,DELETE FROM table;
不是在做所有相同的工作,如果我跟进VACUUM (FULL, ANALYZE) table;
它是一个更昂贵的 21 毫秒,所以DELETE
如果我实际上不需要原始的桌子,这只是一个胜利。
TRUNCATE table;
做更多的固定成本工作和家务比DELETE
相比之下,aTRUNCATE
必须做很多工作。它必须为表、它的 TOAST 表(如果有)以及表的每个索引分配新文件。必须将标头写入这些文件,并且系统目录也可能需要更新(在这一点上不确定,尚未检查)。然后它必须用新文件替换旧文件或删除旧文件,并且必须确保文件系统通过同步操作(fsync() 或类似操作)赶上更改,这通常会将所有缓冲区刷新到磁盘. 如果您使用 (data-eating) option 运行,我不确定是否会跳过同步fsync=off
。
我最近了解到,TRUNCATE
还必须刷新所有与旧表相关的 PostgreSQL 缓冲区。对于巨大的shared_buffers
. 我怀疑这就是为什么它在您的 CI 服务器上较慢的原因。
余额
无论如何,您可以看到TRUNCATE
具有关联 TOAST 表(大多数都有)和多个索引的表可能需要一些时间。不长,但比DELETE
一张几乎空无一人的桌子要长。
因此,您最好做一个DELETE FROM table;
.
--
注意:在 9.0 之前的数据库上,CLUSTER table_id_seq ON table; ANALYZE table;
或者VACUUM FULL ANALYZE table; REINDEX table;
更接近于TRUNCATE
. impl 在 9.0中VACUUM FULL
更改为更好的。