并发数据库(PostgreSQL)命令在同一事务中

时间:2011-11-01 02:32:29

标签: .net multithreading postgresql transactions thread-safety

我正在编写一个.NET 4应用程序,它将大量数据从文件导入PostgreSQL 9.1数据库。分析表明实际插入数据的DB调用占用了90%以上的时间。数据库服务器似乎是CPU限制的 - 使用所有一个CPU。

如果可能的话,我想通过使用所有CPU来更快地导入数据。输入文件可以在客户端上分解成碎片,所以这通常不会太难,但我想确保如果在导入文件时发生任何错误,那么DB根本不会被修改。为了实现这一点,我在一次交易中进行整个导入。

是否有可能以某种方式将并发命令发送到数据库服务器(以利用其所有CPU),但是仍然确保整个导入成功或没有进行任何更改?据我所知,一个事务不能从多个线程同时运行多个命令,可以吗?我正在使用Npgsql作为ADO.NET提供程序,如果这有所不同。

4 个答案:

答案 0 :(得分:4)

Postgres 9.6 之前,使用标准PostgreSQL的多个线程无法并行处理事务,此功能已添加为"parallel query"

但是,您的INSERT操作受CPU限制似乎很可疑。这里可能会改进一些事情。您如何将数据发送到服务器?基本上有四种方法将INSERT数据放入表中:

  1. 一次一行,VALUES表达式提供文字
  2. 一次多行VALUES表达式
  3. INSERT SELECT(插入0-n行)
  4. COPY
  5. COPY是迄今为止最快的方法。

    • 在庞大的批量INSERT / COPY之前删除索引会更快,然后重新创建它们。增量添加索引元组的效率远低于一次创建索引的效率。

    • 触发器,约束或外键约束是可能会降低您速度的其他因素。也许您可以在批量加载之前禁用/删除并在之后启用/重新创建?

    还有许多设置可以产生重大影响。

    • 您可以关闭fsync and synchronous_commit。 (危险!)

    • 暂时停用autovacuum。之后立即运行ANALYZE。 (小心那些!)

    阅读Postgres Wiki中有关Bulk Loading and RestoresTuning Your PostgreSQL Server的文章,尤其是 checkpoint_segments checkpoint_completion_target 上的段落。

    操作可能不像看起来那样受CPU限制。看看这个paragraph in the PostgreSQL Wiki

    另一个减速源可能是伐木。例如,log_statement = all会产生巨大的日志文件,但代价是单行插入。

    这是PostgreSQL Wiki中的quick method to check all your custom settings

    另一个加快速度的想法,特别是当你无法关闭fsync时。创建一个或多个空临时表,如下所示:

    CREATE TEMP TABLE x_tmp AS SELECT * FROM real_tbl LIMIT 0;
    

    考虑如何处理序列和其他默认值! INSERT将所有数据导入登台表,然后在一个命令中写入目标表。索引和约束再次关闭,但时间要短得多。

    INSERT INTO real_tbl SELECT * FROM x_tmp ORDER BY something;
    DROP TABLE x_tmp;
    

    可能会快得多。务必使用足够的RAM进行各种设置。特别要注意temp_buffers

答案 1 :(得分:0)

另一个行动计划可能是:

  • 定义一个包含输入数据的新非规范化表。我们称之为“分期”
  • 使用多个线程和多个连接填充该表。
  • 数据存在后,使用 real 表中的序列将所需的ID插入到临时表中。
  • 并行工作阶段在此结束。
  • 使用一个事务并执行相应的批量插入语句,将数据从登台表移动到实际表中。
  • 截断登台表。

在这种情况下,您支付更多的IO硬币可以减轻您当前的瓶颈(由于输入处理而受到CPU限制)。

答案 2 :(得分:0)

我会说你应该使用prepared transactions。根据需要并行运行多个,如果它们都进入舞台,它们可以毫无错误地准备好,然后提交准备它们,否则回滚准备它们。

首先必须将max_prepared_transactions设置为大于0的某个值并重新启动postgresql。之后,您可以在单个会话中启动事务,如下所示:

begin; 
select yada; 
insert yada; 
update yada; 
prepare transaction 'mytrans';

此时您将收到通知“PREPARE TRANSACTION”或“ROLLBACK”。如果您从任何准备好的事务中获得ROLLBACK,那么您可以对每个事务进行回滚准备,而不进行任何事务。请注意,您不应该留下大量准备好的交易。

答案 3 :(得分:0)

请注意,pg_restore现在使用多线程模式在恢复压缩转储时从多核架构中获益。所以在几个联系上管理大量进口当然是个好主意。我已经看到了将作业数量设置为2 *核心pg_restore的好收益报告。

但是pg_restore不能在此设置中使用--single-transaction。所以和你一样的问题。您可以使用PREPARE TRANSACTION语句尝试两阶段提交事务,它通常由事务管理器完成,而不是应用程序,但如果其中一个事务导入失败,这可能会帮助您使多个事务无效过程