使用ctid删除Postgres表中的行

时间:2019-04-30 06:07:37

标签: postgresql sql-delete

我们有一个表,记录了近20亿个事件。根据我们的数据模型,每个事件都由4列组合主键唯一标识。除主键外,每个不同的列上都有5个B树索引。因此共有6个B树索引。

该事件记录了数年之久,现在我们需要删除1年以上的数据。

我们有一个time列,其中记录了每个事件的长值。我们使用以下查询,

delete from events where ctid = any ( array (select ctid from events where time < 1517423400000 limit 10000) )

索引会更新吗?

在测试过程中没有。

插入后,

total_table_size - 27893760 table_size - 7659520 index_size - 20209664

删除后,

total_table_size - 20226048 table_size - 0 index_size - 20209664

2 个答案:

答案 0 :(得分:0)

考虑使用@a_horse_with_no_name方法是很好的解决方案。

我们所拥有的: Postgres版本9.4。 1 table行,其中2 billion行与21 columns(全部bigint)和5 columns行合并,主键和5各个列索引的日期跨度为2年。

它看起来类似于带有time列的时间序列数据,其中包含UNIX时间戳,但其分析项目除外,因此时间没有有序增加。该表仅是插入和选择的(大多数选择查询使用聚合函数)。

我们需要的:我们的数据跨度为6个月,需要删除旧数据。

我们所做的(对Postgres内部的了解较少): 以10000批处理速率删除行。

初始时,删除ms是如此之快,因为膨胀增加,每批删除增加到近10s。然后autovacuum被触发,并且几乎运行了3 months。插入率很高,每次删除批次也增加了WAL大小。表格中的统计信息不佳使当前查询变得如此缓慢,以至于它们只能运行数分钟和数小时。

因此,我们决定进行分区。我们在9.4中使用表继承进行了实现。

注意:Postgres从版本10开始Declarative Partitioning,可处理使用表继承进行分区所需的大多数手动工作。

请仔细阅读official docs,因为它们有明确的解释。

简化以及我们如何实现它:

  1. 创建父表
  2. 创建带有检查约束的子表。 (我们每月进行分区,并使用时间表创建)
  3. 需要为每个子表分别创建索引
  4. 要删除旧数据,只需删除表,因此不需要抽真空,并且可以立即清除。
  5. 确保将postgres属性constraint_exclusion设置为partition
  6. VACUUM ANALYZE开始插入新分区后的旧分区。 (在我们的例子中,它帮助查询计划者使用仅索引扫描而不是顺序扫描)
  7. 使用文档中提到的触发器可能会使插入速度变慢,因此我们偏离了它,因为我们基于time进行分区,所以我们根据time值在应用程序级别计算了表名每次插入之前都不会影响我们的插入率。

还请阅读此处提到的其他注意事项。

答案 1 :(得分:-1)

可以重新索引

Command:     REINDEX
Description: rebuild indexes
Syntax:
REINDEX { INDEX | TABLE | DATABASE | SYSTEM } name [ FORCE ]