【发布时间】:2020-01-30 03:28:35
【问题描述】:
背景
目前我有一个 postgres 数据库,在公共架构中有大约 100 个表。在向其中一个大表(大约 400 万个)插入数据时(插入冲突),其性能非常缓慢。当我在同一个数据库的不同架构中创建一个类似的表副本(相同的表架构)时,upsert 查询性能显着提高(6 分钟到 3 分钟以下)。
使用的代码
创建表(键是基本哈希):
CREATE TABLE test_table(
id SERIAL,
...,
key CHARACTER VARYING,
CONSTRAINT primary_key_constraint PRIMARY KEY (id),
CONSTRAINT key_uniqueness_constraint UNIQUE (key)
);
使用与 test_table 相同的模式将数据上传到虚拟表以进行 upsert:
COPY dummy_table(
...,
key
)
FROM '<path-to-csv>' DELIMITER ',' CSV HEADER;
要更新数据:
INSERT INTO test_table(
...,
key
)
SELECT
...,
key
FROM
dummy_table
ON CONFLICT(key)
DO UPDATE SET
... = "some-change"
为了实际测试有冲突的 upsert,我将 dummy_table 中的一半数据插入 test_table,然后在查询完成后将其余数据插入。
注意事项
-
VACUUM FULL和VACUUM ANALYZE在公共架构上运行,测试前有 100 个表 - 数据库也在测试前重新索引
-
EXPLAIN ANALYSE给出了两个相同的查询计划,除了一个运行得慢得多
注意事项: - 数据库中的所有表,除了新模式中的测试表,都在公共模式中 - [更新] 我现在意识到,在进行测试之前,现有表已被大量插入。两个表中的数据还是完全一样的。
解释(分析,缓冲区)输出
Insert on test_table (cost=0.00..189096.00 rows=4000000 width=221) (actual time=1228323.809..1228323.809 rows=0 loops=1)
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: key_uniqueness_constraint
Tuples Inserted: 0
Conflicting Tuples: 4000000
Buffers: shared hit=69229375 read=3940270 dirtied=3756690 written=3290015
-> Seq Scan on all_data (cost=0.00..189096.00 rows=4000000 width=221) (actual time=76.951..15397.431 rows=4000000 loops=1)
Buffers: shared hit=4000008 read=129100 dirtied=19 written=3
Planning Time: 157.181 ms
Execution Time: 1228335.801 ms
Insert on test_table (cost=0.00..189033.00 rows=4000000 width=221) (actual time=260305.406..260305.406 rows=0 loops=1)
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: key_uniqueness_constraint
Tuples Inserted: 0
Conflicting Tuples: 4000000
Buffers: shared hit=72766787 read=560868 dirtied=431829 written=265784
-> Seq Scan on all_data (cost=0.00..189033.00 rows=4000000 width=221) (actual time=44.326..15560.603 rows=4000000 loops=1)
Buffers: shared hit=4000008 read=129036 dirtied=8
Planning Time: 88.202 ms
Execution Time: 260312.309 ms
TL;DNR
公共架构中的表 upsert 性能很差,将表转移到同一数据库中的全新架构,性能显着提高。
【问题讨论】:
-
与此类问题一样,如果没有
EXPLAIN (ANALYZE, BUFFERS)两个查询的输出,则无法处理。 -
@LaurenzAlbe 道歉。我已经更新了我的问题以包含 EXPLAIN (ANALYZE, BUFFERS) 输出
-
对于这样一个简单的语句,我发现 157 毫秒(甚至 88 毫秒)的计划时间太高了。您的确切 Postgres 版本是什么?
-
@a_horse_with_no_name 11.2
-
谢谢。不幸的是,执行计划没有提供很多关于
UPDATE的线索。事实上,大部分时间都花在了更新上。突出的是大量的块:表是不是很臃肿,或者你用一个小的fillfactor定义它,还是有很多列?既然你说表定义是相同的,你能用pg_dump -s -t <tablename> <dbname>转储它们并检查差异吗?
标签: postgresql