如果其他列被索引,Postgres 更新速度会慢吗?

Are*_*bre 4 postgresql performance index update postgresql-performance

某些更新在大型 Postgres 表上花费的时间太长。鉴于这些条件:

  • 仅更新一列,且未建立索引
  • 由于之前的更新,该列中的每一行都已包含数据
  • 数据的大小没有改变(例如,重写布尔值)
  • 此表或任何其他表中没有其他列依赖于正在更新的列的值
  • 没有对数据库执行其他查询(这是工作站上的个人研究数据库,而不是企业数据库)
  • 其他列上有索引
  • 带 Bitlocker 的旋转驱动器(非 SSD)和带 Windows 8.1 x64 的快速 PC
  • 该表有 1000 万行和 60 列

...您可能会认为,相对于使用 Bitlocker 旋转媒体的预期,更新将花费合理的时间。我们不会创建更多数据,因此不需要在 HDD 上移动现有数据,只需覆盖它即可。其他索引应该不需要更改。等等,相反,经过20个小时不断的硬盘磨练,我厌倦了等待,停止了查询。如果我删除其他列上的所有索引并重新运行查询,则只需要大约 30 分钟。

为什么与此查询无关的列上的索引会使更新时间膨胀?

Cra*_*ger 5

我们不会创建更多数据,因此不需要在 HDD 上移动现有数据,只需覆盖它即可

事实并非如此。

为了支持回滚和崩溃安全,PostgreSQL 必须为每个修改的行编写一个新副本,而不是就地修改该行。实际上两次,因为它必须先写入 WAL(用于崩溃恢复的顺序日志),然后写入表。

PostgreSQL 的 autovacuum 随后出现,并将旧行版本标记为可以重复使用的可用空间。

有关并发控制和 MVCC 的更多信息,请参阅用户手册。

数据的大小没有改变(例如,重写布尔值)

无关紧要,因为无论如何都必须重写该行。

仅更新一列,并且该列未建立索引,
其他列上有索引

这仅在影响热更新时才重要,如果没有修改索引列并且同一磁盘页(8k 块)上有足够的可用空间来存储行更新的新副本,PostgreSQL 可能会避免为行更新写入新索引条目。排。

为什么与此查询无关的列上的索引会使更新时间膨胀?

在大多数情况下,即使您没有修改索引列,PostgreSQL 也必须添加新的索引条目,因为它必须将行的新版本写入单独的数据库页。

设置FILLFACTORof50或 less 可以对此有所帮助,因为它鼓励 PostgreSQl 保留更多可用空间用于更新,但代价是扫描必须读取和处理更多数据。