68

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

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

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

TRUNCATE TABLE table1, table2  -- ... etc

执行截断需要超过 1 秒。在我们的 CI 服务器(Ubuntu 10.04 LTS)上跟踪日志,截断表需要整整 8 秒,构建需要 84 分钟。

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

一种。它比我的带有 2gb ram
b 的 Macbook Air 慢得多。当postgresql 文档 明确指出它应该更快时,TRUNCATION 比 DELETE 慢得多。

有什么想法吗?

4

4 回答 4

155

这在 SO 和 PostgreSQL 邮件列表上都出现了几次。

最后两点的TL;DR :

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

(b)TRUNCATE具有固定成本,但不一定比 慢,而且DELETE它做更多的工作。请参阅下面的详细说明。

更新:这篇文章引起了关于 pgsql 性能的重要讨论。看到这个线程

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

TRUNCATEvs的详细解释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

关键是做不同的事情DELETETRUNCATE所以你不只是比较两个具有相同结果的命令。

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;TRUNCATEDELETE.

在我在这里的测试中,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更改为更好的。

于 2012-07-11T00:24:39.190 回答
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

我很抱歉写这个作为答案,但我没有找到任何评论链接,可能是因为那里已经有太多评论了。

于 2012-07-14T10:50:25.820 回答
1

我最近遇到了类似的问题,即:

  1. 运行使用 DatabaseCleaner 的测试套件的时间在具有可比硬件的不同系统之间差异很大,
  2. 更改 DatabaseCleaner 策略以:deletion提供约 10 倍的改进。

缓慢的根本原因是用于数据库存储的带有日志 (ext4) 的文件系统。在 TRUNCATE 操作期间,日志守护程序 (jbd2) 使用了大约 90% 的磁盘 IO 容量。在这些情况下,我不确定这是错误、边缘情况还是实际上是正常行为。然而,这解释了为什么 TRUNCATE 比 DELETE 慢得多——它产生了更多的磁盘写入。因为我不想实际使用 DELETE,所以我求助于设置fsync=off,这足以缓解这个问题(在这种情况下,数据安全并不重要)。

于 2018-04-28T09:45:29.017 回答
0

需要考虑的几种替代方法:

  • 创建一个包含静态“夹具”数据的空数据库,并在其中运行测试。完成后,只需删除数据库,这应该很快。
  • 创建一个名为“test_ids_to_delete”的新表,其中包含表名和主键 ID 列。更新您的删除逻辑以在此表中插入 ids/表名称,这将比运行删除快得多。然后,编写一个脚本以“离线”运行以实际删除数据,无论是在整个测试运行完成后,还是在一夜之间。

前者是一种“洁净室”方法,而后者意味着会有一些测试数据会在数据库中保留更长时间。带有脱机删除的“脏”方法是我用于具有大约 20,000 个测试的测试套件的方法。是的,由于开发数据库中有“额外”的测试数据,有时会出现问题。但有时这种“肮脏”帮助我们找到并修复了错误,因为“混乱”更好地模拟了真实世界的情况,以一种洁净室方法永远不会的方式。

于 2012-07-10T18:39:29.660 回答