【问题标题】:Different performance of query run against two tables with the same data针对具有相同数据的两个表运行不同性能的查询
【发布时间】: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 FULLVACUUM 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 &lt;tablename&gt; &lt;dbname&gt; 转储它们并检查差异吗?

标签: postgresql


【解决方案1】:

如果更改fillfactor 有帮助,这是我从您的评论中读出的,那么它一定是PostgreSQL“写放大”问题:

  • 每次更新都会写入一个新版本的行

  • 因此,表上的每个索引也必须获得一个指向新行的新条目,这会导致比其他数据库更多的写入和更差的性能

解决办法是

  • 降低填充因子和

  • 确保没有修改的列被索引

然后 PostgreSQL 可以使用 HOT(“仅堆元组”)更新,它从原始 item 指针“重定向”到新的行版本,因此完全避免修改索引。

【讨论】:

  • 非常感谢您的帮助!关于问题诊断,您已经一针见血了。我在发生此问题的生产表中使用了cluster,这使性能符合应有的水平。您对fillfactor 的建议有望使性能不会降低。
猜你喜欢
  • 1970-01-01
  • 2010-12-29
  • 1970-01-01
  • 2013-12-23
  • 2013-11-22
  • 2021-06-13
  • 1970-01-01
  • 1970-01-01
  • 2020-03-28
相关资源
最近更新 更多