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

Ash*_*Ash 219 postgresql bulkinsert

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

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

Dan*_*Lew 194

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

  • 我在http://stackoverflow.com/questions/12206600/how-to-speed-up-insertion-performance-in-postgresql中详细说明了一些细节. (29认同)
  • @CraigRinger哇,"更详细一点"是我整周看到的最好的轻描淡写;) (21认同)

Ben*_*per 74

还有一种使用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');
Run Code Online (Sandbox Code Playgroud)

上面的代码插入了两行,但你可以任意扩展它,直到你达到准备好的语句令牌的最大数量(它可能是999美元,但我不是100%肯定).有时一个人不能使用COPY,这是一个值得替换的情况.

  • COPY比插入多行值快许多倍. (12认同)
  • 你知道这种方法的性能与COPY的比较吗? (10认同)
  • 这里的表演对我来说是完美的。3.291 秒内 370K 行。 (5认同)
  • COPY 比扩展 INSERT 快很多 (3认同)

Dan*_*ane 20

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


ndp*_*dpu 14

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

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

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
);
Run Code Online (Sandbox Code Playgroud)

批量更新的语法相同:

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;
Run Code Online (Sandbox Code Playgroud)


Mik*_*e T 10

您可以使用COPY table TO ... WITH BINARY" 比文本和CSV格式快一些".如果您要插入数百万行,并且您对二进制数据感到满意,则只能执行此操作.

这是Python中的一个示例配方,使用psycopg2和二进制输入.


wil*_*ser 8

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

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

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

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


Pet*_*uss 6

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

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

带有一些转换的批量插入

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

CSV 示例。假设tablename (x, y, z)SQL 和一个 CSV 文件,如

fieldname1,fieldname2,fieldname3
etc,etc,etc
... million lines ...
Run Code Online (Sandbox Code Playgroud)

您可以使用经典SQLCOPY负载(原始数据)到tmp_tablename,它们插入过滤后的数据为tablename......但是,为了避免磁盘消耗,最好是直接摄入

fieldname1,fieldname2,fieldname3
etc,etc,etc
... million lines ...
Run Code Online (Sandbox Code Playgroud)

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

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

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

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');
Run Code Online (Sandbox Code Playgroud)

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

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
;
Run Code Online (Sandbox Code Playgroud)

缺少 gzip 流

“文件摄取”(主要是在大数据中)最常见的方法是将原始文件保存为 gzip 格式并使用流算法传输它,任何可以在 unix 管道中快速运行且不消耗磁盘的东西:

 gunzip remote_or_local_file.csv.gz | convert_to_sql | psql 
Run Code Online (Sandbox Code Playgroud)

所以理想(未来)是format 的服务器选项.csv.gz


小智 5

我用本机libpq方法实现了非常快速的Postgresq数据加载器.试试我的软件包https://www.nuget.org/packages/NpgsqlBulkCopy/


小智 5

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

$ createdb test 
$ csvsql --db postgresql:///test --insert examples/*.csv
Run Code Online (Sandbox Code Playgroud)