【问题标题】:Bulk insert of hundreds of millions of records批量插入数亿条记录
【发布时间】:2011-02-20 00:11:34
【问题描述】:

将 2.37 亿条记录插入具有规则(用于跨子表分布数据)的表中的最快方法是什么?

我已经尝试或考虑过:

  1. 插入语句。
  2. 事务性插入(BEGINCOMMIT)。
  3. COPY FROM 命令。
  4. http://pgbulkload.projects.postgresql.org/

插入速度太慢(四天),COPY FROM 忽略规则(还有其他问题)。

示例数据:

station_id,taken,amount,category_id,flag
1,'1984-07-1',0,4,
1,'1984-07-2',0,4,
1,'1984-07-3',0,4,
1,'1984-07-4',0,4,T

表结构(包括一条规则):

CREATE TABLE climate.measurement
(
  id bigserial NOT NULL,
  station_id integer NOT NULL,
  taken date NOT NULL,
  amount numeric(8,2) NOT NULL,
  category_id smallint NOT NULL,
  flag character varying(1) NOT NULL DEFAULT ' '::character varying
)
WITH (
  OIDS=FALSE
);
ALTER TABLE climate.measurement OWNER TO postgres;

CREATE OR REPLACE RULE i_measurement_01_001 AS
    ON INSERT TO climate.measurement
   WHERE date_part('month'::text, new.taken)::integer = 1 AND new.category_id = 1 DO INSTEAD  INSERT INTO climate.measurement_01_001 (id, station_id, taken, amount, category_id, flag)
  VALUES (new.id, new.station_id, new.taken, new.amount, new.category_id, new.flag);

数据最初在 MySQL 中,但出于性能原因(并利用 PL/R 扩展)必须切换到 PostgreSQL。

谢谢!

【问题讨论】:

  • 将 2.37 亿行插入到任何表中,无论是否有规则都需要一段时间......这是一大堆数据。
  • 4 天有点过头了,是的...不知道,以前没有使用过 Postgre。祝你好运。我假设这是一次一次性的操作?有什么东西阻止你让它运行 4 天吗?

标签: postgresql bulkinsert


【解决方案1】:

将您的输入拆分为数据库外部的单独文件,然后使用 COPY 上传每个文件,而不是依赖规则来分发它们。如果您给出的规则是任何示例,那么这是一个微不足道的文本转换应用。此外,如果您的磁盘系统能够满足要求,预先拆分将使您可以并行加载拆分文件。

说真的,不要依赖规则为批量加载执行此分配。实际上,批量加载和事务加载需要不同的方法几乎总是如此,除非您准备好暴力破解其中一种(通常是等待)。

例如,您的规则使用 date_part() 从日期中提取月份,因此为了确定子表,postgres 需要分析日期字符串,将其转换为时间戳,然后将时间戳转换回一个日历,只是为了让月份字段再次退出。但是,如果您正在编写一些东西来预先完成此操作,您可以直接执行 substr($date,5,2)(或等效项):您认为哪个会更快?

这也是一个清理数据格式的机会,以便 COPY 接受它。请注意,您可以使用 COPY 命令指定列:如果您没有使用该架构和示例文件执行此操作,则会由于前面的额外“id”列而出错。 (“从 ... 复制带有 csv 标头”可能已经解决了这个问题,但也许不是...“标头”选项可能只是让它跳过第一行)。

我自己在几个小时内将大约 280e6 行加载到了一个 postgresql 实例中,所以这当然不是不可能的。对于这个初始加载,我已经打开了 fsync=off;计划是加载积压,然后再次打开它以进行常规的日常加载。我必须设置 checkpoint_segments=40 以避免在日志中收到检查点警告。这只是被加载到我的开发机器上-我正在为数据库使用专用磁盘,这与用于 xlogs 的磁盘不同(即我在大磁盘上创建了一个表空间并在该表空间内创建了数据库)。该实例的 shared_buffers 设置为 1Gb,checkpoint_target 设置为 0.5。我尝试并行加载一些分区,但并没有带来太大的改进,所以我怀疑慢速磁盘是瓶颈,而不是数据库本身。

还有 1.7e9 行……我希望明天某个时候完成。

【讨论】:

    【解决方案2】:
    1. 创建没有任何索引的父表,只有列和类型(创建表 some_data (c_1 int, c_2 varchar,....))
    2. 为新数据表枚举创建序列
    3. 从序列中获取新 ID
    4. 使用“like”关键字为真实数据创建新表(创建表 some_data_X like some_data)
    5. 在 some_data_X 中插入真实数据,并以二进制格式复制
    6. 创建索引、时间限制等(使用与 postgresql 的多个连接来授权您的核心)
    7. 继承父表
    8. 现在可以选择了! 通过这种方式,我在 10 列(2 xeon、24 核、24 Gb 内存、SSD)上创建了索引,实现了每秒 400000-500000 次插入。

    奖励:在单独的线程中删除旧数据(some_data_X with min X):带有索引的巨大循环缓冲区!

    【讨论】:

      【解决方案3】:

      PostgreSQL 文档包含一个关于 populating a database 的页面,一旦您按照 araqnid 的建议对输入进行预处理以便您可以使用 COPY,它可能会对您有所帮助。

      【讨论】:

        猜你喜欢
        • 2020-02-21
        • 2020-03-18
        • 1970-01-01
        • 2017-07-23
        • 1970-01-01
        • 2012-05-30
        • 1970-01-01
        • 1970-01-01
        • 2017-12-29
        相关资源
        最近更新 更多