【问题标题】:pg_restoring very large single table using -j option taking several hourspg_restoreing 非常大的单个表使用 -j 选项需要几个小时
【发布时间】:2018-02-10 20:33:29
【问题描述】:

我正在处理具有 700+ 百万行的单个表(没有分区)。我想将此数据加载到另一个数据库,所以我使用了以下 pg_dump 命令,

pg_dump -Fc --column-inserts --data-only --table='tname' -U 
postgres -d dbname > /root/tname_experiment_inserts_custom_format.dump

在目标系统上我使用了以下命令,

pg_restore -d dest_dbname -U postgres -j 7 /root/tname_experiment_inserts_custom_format.dump

目标数据库已经有我试图恢复的表,所以我使用了 TRUNCATE,然后删除了所有索引。目标系统有 32GB 物理内存,我在 postgres 配置文件中做了如下设置,

log_min_duration_statement = -1
autovacuum = off
maintenance_work_memory = 7gb 
wal_level = minimal
fsync = off
full_page_writes= off
synchronous_commit= off
max_wal_size= 20GB
wal_buffers= 16MB

当我为 pg_restore 计时时,一个小时内只有大约 1600 万行被插入。这意味着恢复数据需要 40 多个小时 (!)。之后,我必须创建我删除的索引和外部约束,这可能需要几个小时。我有一种感觉,我可以做一些不同的事情来使整个过程更快。请给我任何可以帮助我提高此过程效率的指示。我还想提一下,我已经看过 COPY 但因为它不保持主键的顺序,所以这个选项对我不利。如果我不知道保留数据顺序的 COPY 的任何特殊设置,那么很高兴知道!

整个事情的目的是更改列的某些数据类型,当使用 alter table alter column query 完成时,这些数据类型也需要类似的时间。

【问题讨论】:

  • 省略--column-inserts
  • @wildplasser 非常感谢您的回复。我可以知道pg_dump中的--column-inserts减慢pg_restore的原因吗?请随时向我指出相关文档。 (不在办公室,所以无法早点回复)
  • 其实用--data-only 是不是和“copy”一样?如果是这样,那么它将不会维护键的顺序,然后依赖于该表的另一个表将需要手动修改。就像我说的,我只想修改一个表,而许多其他表不会被触及。我只是在还原之前删除外部约束并将它们添加回来,因此键顺序对我来说很重要。
  • 注:没有顺序。任何订单都有效。

标签: postgresql pg-dump pg-restore


【解决方案1】:

鉴于此表:


CREATE TABLE abc(a serial NOT NULL
        ,b text
        ,c DATE NOT NULL
        );
INSERT INTO abc(b,c) VALUES
        ('cow' , '2017-01-01')
        , ('pig' , '2017-01-02')
        , ('dog' , '2017-01-03')
        , ('cat' , '2017-01-04')
        ;

pg_dump -U postgres mydb --column-inserts --data-only

会产生这种输出:


--
-- Data for Name: abc; Type: TABLE DATA; Schema: tmp; Owner: postgres
--

INSERT INTO abc (a, b, c) VALUES (1, 'cow', '2017-01-01');
INSERT INTO abc (a, b, c) VALUES (2, 'pig', '2017-01-02');
INSERT INTO abc (a, b, c) VALUES (3, 'dog', '2017-01-03');
INSERT INTO abc (a, b, c) VALUES (4, 'cat', '2017-01-04');

--
-- Name: abc_a_seq; Type: SEQUENCE SET; Schema: tmp; Owner: postgres
--

SELECT pg_catalog.setval('abc_a_seq', 4, true);

省略--colum-inserts 将产生:


--
-- Data for Name: abc; Type: TABLE DATA; Schema: tmp; Owner: postgres
--

COPY abc (a, b, c) FROM stdin;
1   cow 2017-01-01
2   pig 2017-01-02
3   dog 2017-01-03
4   cat 2017-01-04
\.

--
-- Name: abc_a_seq; Type: SEQUENCE SET; Schema: tmp; Owner: postgres
--

SELECT pg_catalog.setval('abc_a_seq', 4, true);

所以--column-inserts 将为每一行生成一个插入语句,这非常慢。 --data-only 标志仅禁止生成 DDL 以创建表。

【讨论】:

  • 好的,我的意思是我想要相同的主键。但我猜这会很好用。谢谢!
【解决方案2】:

如上所述,使用产生 COPY 语句的 pg_dump。

此外,在大量写入流量下,大量数据将写入 WAL,因此您希望 WAL 位于单独的快速磁盘或 SSD 上。

另一个选项是设置它,以便不会将任何内容写入 WAL。此导入是一个全有或全无的操作。要么它工作,要么你会截断并再次运行它。因此,将资源专用于 WAL 以确保表是防崩溃和 ACID 是没有意义的(除非服务器是复制主机)。

有两种方法可以做到这一点:

  • 删除表,然后确保整个还原发生在一个事务中。 “开始;创建表...复制...提交。”在这种情况下,不会向 WAL 写入任何数据。这是因为如果事务失败,则根本没有创建表。所以它不需要是 ACID。
  • 如果这是一个测试服务器,而您只是想使用数据,您可以将所有内容设置为 UNLOGGED,但当然所有数据都会在崩溃的情况下被清除。

现在,COPY 不会被并行化,但一旦加载数据,索引和约束创建就可以并行化。因此,如果您使用 pg_restore 来恢复索引,请确保使用 -j 选项来使用您的核心。如果不这样做,只需打开几个 psql 窗口即可。

maintenance_work_mem 的调整对索引创建也有很大帮助。如果服务器什么都不做,只是恢复,使用 RAM 进行排序和创建索引确实会加快速度。

【讨论】:

  • 好的@peufeu。感谢您对 session 的指点,而且我并没有真正想到我也可以使用 pg_restore 来创建索引。
猜你喜欢
  • 2022-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多