使用Java线程并行插入数据库

时间:2012-03-13 00:01:34

标签: java multithreading jdbc

我有一个Java程序,需要在SQL Server数据库中插入大量的大行。行数为800k,每个行的大小约为200字节。

目前它们被分成50个批次,然后使用单个语句插入每个批次。 (我们已经通过JTDS记录确认每个批次都使用一个sp_exec调用。)调整25到250之间的批量大小似乎没有任何显着影响,50几乎是最佳的。

我已尝试将批次划分为(例如)5组,并使用线程并行处理每个组。这明显更快 - 使用5个线程的速度快了两倍。

我的问题是如何使线程使用健壮。特别是,如果任何批次失败,则会抛出异常。我希望将该异常捕获并传递给调用者,并且我希望在我们传递之前100%确定其他线程已完成(中止或完成)。因为在程序后期从异常中恢复时,我们不希望意外的行继续到达表中。

这就是我所做的:

/** Method to insert a single batch. */
private void insertBatchPostings(Collection<Posting> postings) throws PostingUpdateException
{
    // insert the batch using a single INSERT invokation
    // throw a PostingUpdateException if anything goes wrong
}

private static final int insertionThreads = 5;

/** Method to insert a collection of batches in parallel, using the above. */
protected void insertBatchPostingsThreaded(Collection<Collection<Posting>> batches) throws PostingUpdateException
{
    ExecutorService pool = Executors.newFixedThreadPool(insertionThreads);
    Collection<Future> futures = new ArrayList<Future>(batches.size());

    for (final Collection<Posting> batch : batches) {
        Callable c = new Callable() {
            public Object call() throws PostingUpdateException {
                insertBatchPostings(batch);
                return null;
            }            
        };
        /* So we submit each batch to the pool, and keep a note of its Future so we can check it later. */
        futures.add(pool.submit(c));
    }

    /* Pool is running, indicate that no further work will be submitted to it. */
    pool.shutdown();

    /* Check all the futures for problems. */
    for (Future f : futures) {
        try {
            f.get();
        } catch (InterruptedException ex) {
            throw new PostingUpdateException("Interrupted while processing insert results: " + ex.getMessage(), ex);
        } catch (ExecutionException ex) {
            pool.shutdownNow();
            throw (PostingUpdateException) ex.getCause();
        }
    }
}

当它返回时我想保证所有线程都处于休眠状态。

问题

(我想澄清一下我到底要问的是什么。)

  1. 以上代码是否完全健壮,因为在insertBatchPostingsThreaded返回后没有线程插入会继续运行?
  2. 是否有更好,更简单的方法来使用Java并发功能来实现这一目标?我的代码对我来说看起来过于复杂(引起了对遗漏边缘案例的怀疑)。
  3. 任何一个线程失败后立即让它失败的最佳方法是什么?
  4. 我不是一个天生的Java程序员,所以我希望最终得到一些不会宣传这一事实的东西。 :)

1 个答案:

答案 0 :(得分:1)

Guava的Futures.successfulAsList将期货列表作为输入并返回未来“其价值是包含其所有成功输入期货价值的列表。”您可以在生成的get()上调用Future,然后浏览原来的未来列表以检查是否有任何失败。