在Postgres中批量插入的最快方法是什么?

时间:2009-04-17 03:50:47

标签: postgresql bulkinsert

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

有没有更好的方法来做到这一点,我不知道一些批量插入语句?

10 个答案:

答案 0 :(得分:180)

PostgreSQL最初如何最好地填充数据库a guide,他们建议使用COPY命令批量加载行。该指南还提供了一些关于如何加快进程的其他好技巧,比如在加载数据之前删除索引和外键(并在之后添加它们)。

答案 1 :(得分:69)

还有一种使用COPY的替代方法,它是Postgres支持的多行值语法。来自documentation

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,这是对这些情况的有效替代。

答案 2 :(得分:19)

加快速度的一种方法是在事务中明确执行多个插入或复制(比如1000)。 Postgres的默认行为是在每个语句之后提交,因此通过批量提交,可以避免一些开销。正如Daniel的回答所说,您可能必须禁用自动提交才能使用。另请注意底部的注释表明将wal_buffers的大小增加到16 MB也可能有所帮助。

答案 3 :(得分:10)

带有数组的

UNNEST函数可以与多行VALUES语法一起使用。我认为这个方法比使用COPY慢,但是在使用psycopg和python时很有用(传递给list的python cursor.execute变成了pg {{1} }):

ARRAY

没有INSERT INTO tablename (fieldname1, fieldname2, fieldname3) VALUES ( UNNEST(ARRAY[1, 2, 3]), UNNEST(ARRAY[100, 200, 300]), UNNEST(ARRAY['a', 'b', 'c']) ); 使用带有额外存在检查的subselect:

VALUES

与批量更新相同的语法:

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
);

答案 4 :(得分:9)

您可以使用“somewhat faster than the text and CSV formatsCOPY table TO ... WITH BINARY。只有在要插入数百万行时才能执行此操作,并且如果您对二进制数据感到满意。

这是example recipe in Python, using psycopg2 with binary input

答案 5 :(得分:7)

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

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

然后我只是做一个“插入目标select * from tmp”或类似的东西。

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

答案 6 :(得分:5)

我使用原生libpq方法实现了非常快速的Postgresql数据加载器。 试试我的包https://www.nuget.org/packages/NpgsqlBulkCopy/

答案 7 :(得分:5)

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

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

答案 8 :(得分:2)

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

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

批量插入并进行一些转换

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

CSV示例。假设在SQL上使用tablename (x, y, z)和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准备数据库,而可以使用a function that generates it来使用静态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.jsonRanger_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流

最常见的“文件提取”方法(主要是在大数据中)是将原始文件保留在gzip format上,并通过streaming algorithm进行传输,任何可以快速运行且在unix管道中不会消耗磁盘的东西:

 gunzip remote_or_local_file.csv.gz | convert_to_sql | psql 

理想的(未来)是格式为.csv.gz服务器选项

答案 9 :(得分:0)

可能我已经迟到了。但是,Bytefish 有一个名为 pgbulkinsert 的 Java 库。我和我的团队能够在 15 秒内批量插入 100 万条记录。当然,我们还执行了一些其他操作,例如,从位于 Minio 上的文件中读取 1M+ 条记录,在 1M+ 条记录的顶部进行一些处理,过滤掉重复的记录,最后将 1M 条记录插入 Postgres 数据库.所有这些过程都在 15 秒内完成。我不记得具体执行数据库操作需要多少时间,但我认为大约不到 5 秒。从 https://www.bytefish.de/blog/pgbulkinsert_bulkprocessor.html

中查找更多详细信息