我的 postgres 批量插入的瓶颈是什么?

问题描述 投票:0回答:1

我正在将一些数据批量插入到 RDS 上的 postgres 数据库中,但花费的时间比我想要的要长。

初始化数据库模式的迁移如下所示:

CREATE TABLE "addresses" ("address" TEXT NOT NULL);

CREATE UNIQUE INDEX "unique_address" ON "addresses"("address");

CREATE INDEX "autocomplete_index" ON "addresses" USING btree (lower(address) text_pattern_ops);

数据来自 S3,其中我收集了大约 800 个 256MB 的 CSV 文件。对于每个 CSV 文件,我使用

aws_s3.table_import_from_s3
函数将数据复制到临时表中。这部分非常快。然后我从临时表插入到我的
addresses
表中,如下所示:

INSERT INTO addresses
SELECT * FROM temp_addresses
ON CONFLICT (address) DO NOTHING;

此 INSERT 导入单个 256MB csv 文件大约需要 90 分钟。

从性能洞察页面来看,瓶颈似乎是 IO。 (这是我从这里由“IO:DataFileRead”主导的条中推断出的)。

Average Active Sessions

数据库实例是 db.t3.small,具有 2 个 vCPU 和 2 GB RAM、1024 GB gp3 存储、12000 次预配置 IOPS 和 500 MiBps 吞吐量。

据我所知,我的 IO 吞吐量远远低于限制:

IO throughput

...而且我的 IOPS 似乎也远低于限制:

IOPS

...所以我很难理解这里的瓶颈是什么。我错过了什么?


补充说明:

这是负载期间CPU使用率的图表:

CPU Usage

这是加载期间可释放的内存之一:

Freeable Memory

postgresql amazon-web-services amazon-rds database-performance
1个回答
0
投票

您的瓶颈是读取索引页以便更新它们以获取新数据。 这些读取每次读取只需要 8kB,并且(大概)是随机分散的。 这意味着您无法最大化吞吐量,因为这样做需要大量读取或连续读取。 您也无法最大化 IOPS,因为这样做需要同时处理多个 IO 请求,并且单个 PostgreSQL 进程在进行索引维护时不使用异步 IO/预取。

AWS 模糊地将 gp3 延迟描述为“个位数毫秒”。 如果我们假设这意味着 1 毫秒,那么您需要同时至少有 12 个请求才能达到 12k IOPS 的限制。

您可以增加 RAM,以便更多索引可以保留缓存,而不需要访问磁盘,但增加 RAM 足以处理 200GB 的数据似乎不太合理。 但几乎所有实际从磁盘读取的页面都会被弄脏,最终必须写回。 内核可能会很好地在内部吸收这些写入,然后将它们异步发送到底层存储设备——但我不指望它能完美工作。

您可以尝试减少针对 gp3 的 IO 请求的延迟,但我不知道您将如何做到这一点,似乎没有任何配置旋钮可以对此进行调整。

您可以尝试通过一次启动多个工作进程处理不同的文件来同时插入多个文件。 这将是获得更多异步 IO 请求的一种方法。 然而,这也会引入不同的瓶颈,因为现在多个工作人员必须努力工作以避免破坏彼此的内存。

您可以删除不需要的索引(任何非唯一的索引),并仅在所有加载完成后重新添加它们。 或者您可以在加载之前/加载时对数据进行排序,以便数据按索引顺序插入。 这样,索引维护将一遍又一遍地定向到相同的索引叶页,并且大多数时候它会发现热页已经在缓存中,因此不需要从磁盘读取它们。 您可能需要将这些组合起来,删除一些索引并按未删除的索引进行排序。

根据您最近的编辑,看起来像:

INSERT INTO addresses
SELECT * FROM temp_addresses order by address
ON CONFLICT (address) DO NOTHING;

根据数据中的大写模式,对“地址”进行排序可能会为较低的(“地址”)提供足够好的排序,因此可缓存性的好处也将延续到该索引。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.