Postgresql截断速度

bra*_*rad 62 postgresql truncate database-performance

我们正在使用Postgresql 9.1.4我们的数据库服务器.我一直在努力加快我的测试套件的速度,所以我盯着db稍微分析一下,看看到底发生了什么.我们使用database_cleaner在测试结束时截断表.是的我知道交易更快,我不能在某些情况下使用它们所以我不关心它.

我关心的是,为什么TRUNCATION需要这么长时间(比使用DELETE更长)以及为什么它在我的CI服务器上需要更长时间.

现在,在本地(在Macbook Air上)一个完整的测试套件需要28分钟.拖尾日志,每次我们截断表...即:

TRUNCATE TABLE table1, table2  -- ... etc
Run Code Online (Sandbox Code Playgroud)

执行截断需要1秒多的时间.在我们的CI服务器(Ubuntu 10.04 LTS)上记录日志,需要花费整整8秒才能截断表格,构建需要84分钟.

当我切换到:deletion策略时,我的本地构建花了20分钟,CI服务器下降到44分钟.这是一个显着的差异,我真的很惊讶为什么会这样.我已经调整 CI服务器上数据库,它有16GB系统RAM,4gb shared_buffers ......和一个SSD.所有好东西.这怎么可能:

一个.它比我的Macbook Air慢了2gb
.postgresql文档 明确指出它应该快得多时,TRUNCATION比DELETE慢得多.

有什么想法吗?

Cra*_*ger 144

最近出现了几次,无论是在SO还是PostgreSQL邮件列表上.

TL; DR为你的最后两个点:

(a)较大的shared_buffers可能是CI服务器上TRUNCATE较慢的原因.不同的fsync配置或使用旋转介质而不是SSD也可能有问题.

(b)TRUNCATE有固定成本,但不一定慢DELETE,加上它做的更多.请参阅以下详细说明.

更新:一个上的pgsql性能显著讨论这个职位出现.看到这个帖子.

更新2: 9.2beta3已经添加了改进,应该有所帮助,请参阅这篇文章.

TRUNCATEvs的详细解释DELETE FROM:

虽然不是该主题的专家,但我的理解是,TRUNCATE每个表的成本几乎是固定的,而DELETEn行的成本至少为O(n); 更糟糕的是,如果有任何外键引用正在删除的表.

我一直认为a的固定成本TRUNCATE低于DELETE近乎空桌上的成本,但这根本不是真的.

TRUNCATE table; 不止于此 DELETE FROM table;

a之后的数据库状态与TRUNCATE table您运行时的状态非常相似:

  • DELETE FROM table;
  • VACCUUM (FULL, ANALYZE) table; (仅限9.0+,见脚注)

......当然TRUNCATE,实际上并没有实现a DELETE和a的效果VACUUM.

重点是DELETETRUNCATE做不同的事情,所以你不只是比较两个具有相同结果的命令.

A DELETE FROM table;允许死行和膨胀保留,允许索引携带死条目,不更新查询规划器使用的表统计信息等.

A TRUNCATE为您提供了一个全新的表和索引,就像它们刚刚被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 DELETE FROM table;通常比TRUNCATE0.5ms vs 2ms 快4倍.这是SSD上的测试数据库,fsync=off因为我不在乎我是否丢失了所有这些数据.当然,DELETE FROM table;并没有完成所有相同的工作,如果我跟进VACUUM (FULL, ANALYZE) table;一个更昂贵的21毫秒,所以DELETE如果我实际上不需要原始的表,这只是一个胜利.

TRUNCATE table; 做了更多的固定成本工作和家务管理 DELETE

相比之下,TRUNCATE必须做很多工作.它必须为表,TOAST表(如果有)以及表具有的每个索引分配新文件.必须将标头写入这些文件,系统目录也可能需要更新(不确定在那一点,没有检查).然后它用新的来代替旧的文件或删除旧的,并且必须确保文件系统已经赶上了与同步操作的变化- FSYNC()或类似-通常刷新所有缓存到磁盘.如果您使用(数据进食)选项运行,我不确定是否跳过同步fsync=off.

我最近了解到,TRUNCATE还必须刷新与旧表相关的所有PostgreSQL缓冲区.这可能需要花费大量的时间shared_buffers.我怀疑这就是为什么它在你的CI服务器上速度慢的原因.

余额

无论如何,你可以看到TRUNCATE一个表有一个关联的TOAST表(大多数做)和几个索引可能需要一些时间.不长,但比DELETE近乎空的桌子更长.

因此,你可能会做得更好DELETE FROM table;.

-

注意:在9.0之前的DB上,CLUSTER table_id_seq ON table; ANALYZE table;或者VACUUM FULL ANALYZE table; REINDEX table;会更接近于TRUNCATE.VACUUM FULL在9.0中,impl变为更好的一个.

  • 并且它们具有不同类型的锁定:表锁定与行锁定. (4认同)

Sta*_*ich 5

布拉德,只是为了让你知道.我已经深入研究了一个非常相似的问题.

相关问题:30行几行 - TRUNCATE最快的清空方法和重置附加序列?

另请查看此问题和此拉取请求:

https://github.com/bmabey/database_cleaner/issues/126

https://github.com/bmabey/database_cleaner/pull/127

这个主题:http://archives.postgresql.org/pgsql-performance/2012-07/msg00047.php

我很抱歉写这个作为答案,但我没有找到任何评论链接,也许是因为已经有太多的评论.