将大量插入Postgres的最快方法是什么?


241

我需要以编程方式将数千万条记录中的10条插入到postgres数据库中。目前,我正在单个“查询”中执行1000条插入语句。

有一些更好的方法吗,一些我不知道的大容量插入语句?

Answers:


211

PostgreSQL提供有关如何最佳地初始填充数据库的指南,他们建议使用COPY命令批量加载行。该指南还提供了一些其他有关如何加快处理速度的很好的技巧,例如在加载数据之前删除索引和外键(然后再添加回去)。


33
我也在stackoverflow.com/questions/12206600/…中写了更多细节来阐述。
克雷格·林格2014年

24
@CraigRinger Wow,“多一点细节”是我整周见过的最好的轻描淡写了;)
culix 2014年

尝试安装软件包NpgsqlBulkCopy
Elyor

1
-因为索引也用于db记录的物理布局。不知道删除任何数据库中的索引是否是一个好主意。
Farjad 2014年

但是你的推荐,没有什么记忆!!!而且,如果您的批处理大小可以很小,则非常不好,它是类:(我尝试npgsql CopyIn类,因为它就像PG查询语句中的CSV格式映射一样。您可以尝试使用Big Table吗?
Elyor

93

有一种使用COPY的替代方法,它是Postgres支持的多行值语法。从文档中

INSERT INTO films (code, title, did, date_prod, kind) VALUES
    ('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'),
    ('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy');

上面的代码插入了两行,但是您可以任意扩展它,直到达到准备好的语句标记的最大数量(可能是999美元,但我不确定100%)。有时人们不能使用COPY,这对于那些情况是值得的。


12
您知道这种方法的性能与COPY相比如何吗?
格兰特·汉弗莱斯

如果您遇到权限问题,请在尝试此操作之前,先使用COPY ... FROM STDIN
Andrew Scott Evans

如果您使用的是行级安全性,那将是您的最佳选择。从版本12开始“具有行级安全性的表不支持COPY FROM”
。– Eloff

COPY比扩展INSERT快得多
hipertracker

24

一种加快速度的方法是在一个事务中(例如1000)显式执行多次插入或复制。Postgres的默认行为是在每个语句之后提交,因此通过批量提交,可以避免一些开销。正如丹尼尔答案中的指南所述,您可能必须禁用自动提交才能正常工作。还要注意底部的注释,该注释建议将wal_buffers的大小增加到16 MB也可能有帮助。


1
值得一提的是,您可以向同一笔交易添加的插入/副本数量的限制可能远高于您尝试执行的任何操作。您可以在同一事务中添加数百万行,而不会遇到问题。
Sumeet Jain's

@SumeetJain是的,我只是在谈谈每笔事务的复制/插入数量方面的速度“最佳点”。
丹娜桑那

事务运行时,这会锁定表吗?
Lambda Fairy

15

UNNEST带有数组的函数可以与多行VALUES语法一起使用。我认为此方法比使用慢,COPY但对我使用psycopg和python(list传递给cursor.executepg的python ARRAY)很有用:

INSERT INTO tablename (fieldname1, fieldname2, fieldname3)
VALUES (
    UNNEST(ARRAY[1, 2, 3]), 
    UNNEST(ARRAY[100, 200, 300]), 
    UNNEST(ARRAY['a', 'b', 'c'])
);

无需VALUES将subselect与其他存在检查一起使用:

INSERT INTO tablename (fieldname1, fieldname2, fieldname3)
SELECT * FROM (
    SELECT UNNEST(ARRAY[1, 2, 3]), 
           UNNEST(ARRAY[100, 200, 300]), 
           UNNEST(ARRAY['a', 'b', 'c'])
) AS temptable
WHERE NOT EXISTS (
    SELECT 1 FROM tablename tt
    WHERE tt.fieldname1=temptable.fieldname1
);

与批量更新相同的语法:

UPDATE tablename
SET fieldname1=temptable.data
FROM (
    SELECT UNNEST(ARRAY[1,2]) AS id,
           UNNEST(ARRAY['a', 'b']) AS data
) AS temptable
WHERE tablename.id=temptable.id;


9

它主要取决于数据库中的(其他)活动。这样的操作有效地冻结了整个数据库以供其他会话使用。另一个考虑因素是数据模型以及约束,触发器等的存在。

我的第一种方法始终是:创建一个结构类似于目标表的(临时)表(创建表tmp AS select * from target,其中1 = 0),然后从将文件读入临时表开始。然后,我检查可以检查的内容:重复项,目标中已存在的键等。

然后,我只是执行“将目标插入到目标选择* from tmp中”或类似操作。

如果失败或花费太长时间,我将中止它并考虑其他方法(暂时删除索引/约束等)



6

我刚遇到此问题,建议将csvsql发行版)批量导入Postgres。要执行批量插入,您只需简单地createdb使用csvsql,然后使用即可连接到数据库,并为整个CSV文件夹创建单个表。

$ createdb test 
$ csvsql --db postgresql:///test --insert examples/*.csv

1
对于csvsql,为了同时清除源csv的所有可能的格式错误,最好遵循这些说明此处的
2015年

0

外部文件是最好的典型批量数据

术语“大量数据”与“大量数据”相关,因此很自然地使用原始原始数据,而无需将其转换为SQL。“批量插入”的典型原始数据文件为CSVJSON格式。

批量插入并进行一些转换

ETL应用程序和提取过程中,我们需要在插入数据之前对其进行更改。临时表会占用(大量)磁盘空间,但这并不是更快的方法。在PostgreSQL的外商数据封装器(FDW)是最好的选择。

CSV示例。假设tablename (x, y, z)on SQL和CSV文件类似

fieldname1,fieldname2,fieldname3
etc,etc,etc
... million lines ...

您可以使用经典的SQL COPY加载(作为原始数据)到tmp_tablename,它们将过滤后的数据插入到tablename...中,但是,为了避免占用磁盘,最好的方法是直接通过

INSERT INTO tablename (x, y, z)
  SELECT f1(fieldname1), f2(fieldname2), f3(fieldname3) -- the transforms 
  FROM tmp_tablename_fdw
  -- WHERE condictions
;

您需要为FDW准备数据库,而tmp_tablename_fdw可以使用静态函数来生成它

CREATE EXTENSION file_fdw;
CREATE SERVER import FOREIGN DATA WRAPPER file_fdw;
CREATE FOREIGN TABLE tmp_tablename_fdw(
  ...
) SERVER import OPTIONS ( filename '/tmp/pg_io/file.csv', format 'csv');

JSON示例。一组的两个文件,myRawData1.json并且Ranger_Policies2.json可以通过摄入:

INSERT INTO tablename (fname, metadata, content)
 SELECT fname, meta, j  -- do any data transformation here
 FROM jsonb_read_files('myRawData%.json')
 -- WHERE any_condiction_here
;

函数jsonb_read_files()读取文件夹的所有文件(由掩码定义):

CREATE or replace FUNCTION jsonb_read_files(
  p_flike text, p_fpath text DEFAULT '/tmp/pg_io/'
) RETURNS TABLE (fid int,  fname text, fmeta jsonb, j jsonb) AS $f$
  WITH t AS (
     SELECT (row_number() OVER ())::int id, 
           f as fname,
           p_fpath ||'/'|| f as f
     FROM pg_ls_dir(p_fpath) t(f)
     WHERE    f like p_flike
  ) SELECT id,  fname,
         to_jsonb( pg_stat_file(f) ) || jsonb_build_object('fpath',p_fpath),
         pg_read_file(f)::jsonb
    FROM t
$f$  LANGUAGE SQL IMMUTABLE;

缺少gzip流

最常见的“文件提取”方法(主要是在Big Data中)是将原始文件保存为 gzip格式,并使用流算法进行传输,这种方法可以快速运行并且在unix管道中不会消耗磁盘:

 gunzip remote_or_local_file.csv.gz | convert_to_sql | psql 

因此,理想(未来)是format 的服务器选项.csv.gz

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.