【问题标题】:Repetitive Postgres updates of arrays leading to bloat?导致膨胀的数组的重复 Postgres 更新?
【发布时间】:2019-03-10 23:25:14
【问题描述】:

我正在运行一个 Python 脚本,它处理多个不同指标的时间序列数据,然后将结果写入 Postgres 数据库。

时间序列假设 40 个 epoch,在数据库中存储为 real[40] 数组列。

当一次性将所有 40 个 epoch 的输出写入表时(所有行的批量更新),一切似乎都运行良好。即

UPDATE my_table SET
  arr_col_1 = {1, 2, 3, ... 40},
  arr_col_2 = {1, 2, 3, ...40},
  ...
  arr_col_90 = {1, 2, 3, ...40};

但是,将各个时期的结果迭代地写入数组中的每个位置似乎会占用硬盘驱动器上的所有可用空间,例如

UPDATE my_table SET
  arr_col_1[1] = 1,
  arr_col_2[1] = 1,
  ...
  arr_col_90[1] = 1;

UPDATE my_table SET
  arr_col_1[2] = 2,
  arr_col_2[2] = 2,
  ...
  arr_col_90[2] = 2;

-- repeat x 38 more times

迭代策略的原因是为了容纳更多的行,40个epoch的结果不能同时放入内存。

据我所知,UPDATE 查询在某些情况下会删除和重写行数据,但我不清楚何时会发生这种情况以及这可能与数组有什么关系。有没有办法在不导致数据库膨胀的情况下迭代地更新大量行的数组?

【问题讨论】:

  • 您的数据库设计对我来说似乎是错误的。您能否共享完整的表架构以及此表与其他表之间存在哪些关系。粗略地说,您应该将数据库模式降低到 3NF 以进行高效编写。
  • Postgres 支持数组,是的。但是,我们不必(ab)使用来存储这样的数据,忽略更合适的关系数据库规则,这些规则是任何传统模式/表结构设计的基础。
  • 我不需要单独查询时期,所以跨列分散时期真的更好吗?还是我应该改用 JSONB?

标签: postgresql sql-update mvcc


【解决方案1】:

正如其他人正确提到的,这种方法不太适合 PostgreSQL 的操作模式。

但是,您可以使用称为 HOT 的优化:

  • 用小于 100 的 fillfactor 声明您的表,以便 INSERTs 在每个块中留出可用空间:

    ALTER TABLE my_table SET (fillfactor = 50);
    

    此设置仅影响未来的活动,您必须重新组织表格才能影响现有数据。如果您更新表中的每一行,您可能需要一个低至 30 的设置才能生效。

  • 确保更新的列没有有索引。

然后 PostgreSQL 可以使用“HOT update”并即时回收死表条目,这避免了自动清空的需要,这显然无法跟上您的表。

检查您的表的pg_stat_user_tables 行中的n_tup_hot_upd 列,看看它是否正常工作。

【讨论】:

  • 谢谢,我认为这正是我所需要的,因为我目前看不到频繁更新的方法。理想情况下,我会研究一个不同的数据库,但需要 postgis 来处理某些复杂的空间查询。
【解决方案2】:

Postgres 使用 MVCC,它执行写时复制。

UPDATE 将整行复制到新行,旧行被标记为删除,但删除本身只发生在真空期间,由 autovacuum 守护程序定期发生。

你可以通过运行自己释放空间

VACUUM

你有多少磁盘空间用完了?我从未听说过非大型数据库会出现这样的问题。

【讨论】:

  • 感谢您的清晰解释。一直在运行 VACUUM 来清理臃肿。有几十万行,所以大概是对数组的迭代更新阻止了自动清理工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-24
相关资源
最近更新 更多