从笨重的桌子中取出的优化

时间:2014-08-08 18:58:29

标签: java sql performance postgresql optimization

我有一个拥有数百万条记录的PostgreSQL表。我需要处理每一行,为此我使用该表中的列'isProcessed',因此默认情况下它是false,当我处理它时,我将其更改为true

现在问题是记录太多,并且由于例外代码绕过了一些记录而使它们isProcessed=false并且这使得执行速度非常慢。

我正在考虑使用索引但是使用布尔值却没有帮助。

请提供一些优化技巧或更好的实践。

更新

我没有代码,这只是我的同事问我的意见的问题。

3 个答案:

答案 0 :(得分:1)

通常,布尔值上的索引不是一个好主意,但在PostgreSQL中,您可以使用部分索引http://www.postgresql.org/docs/9.3/interactive/indexes-partial.html来创建一个索引,其中只包含一个值的条目。它最终成为你要处理的事物的队列,物品一旦完成就会掉落。

CREATE INDEX "yourtable_isProcessed_idx" ON "public"."yourtable"
USING btree ("isProcessed")
WHERE (isProcessed IS NOT TRUE);

在寻找下一个要处理的项目时,这将使生活更轻松。理想情况下,您应该一次处理多个,特别是如果您可以在单个查询中执行此操作,尽管一次执行数百万次可能会令人望而却步。在那种情况下,你可以做到

update yourtable
set ....
where id in (select  id from yourtable where isProcessed = false limit 100 )

如果你必须一次做一件事,我仍然会限制你检索的东西,所以可能会检索

select id from yourtable where iProcessed = false limit 1

答案 1 :(得分:0)

如果没有看到您的代码,就很难说出真正发生的事情。逐行进行任何处理,听起来就是这样,需要花费很长时间。

一般来说,处理数据的最佳方式是成套。在流程结束时,您最终将拥有一组记录,其中isProcessed需要为真(操作成功的地方),以及需要isProcessed的集合false(操作失败的地方)。在处理数据时,请跟踪哪些记录可以成功更新,哪些记录无法更新。您可以通过创建主键的列表或数组或用于标识行的任何其他数据来完成此操作。然后,在您完成数据处理后,执行一次更新以标记成功的记录,并更新一次更新未成功的记录。这将是更多的代码,但在处理它之后单独更新每一行将会非常慢。

再一次,看到代码会有所帮助,但是如果您在处理完每条记录后对其进行更新,我怀疑这会让您放慢速度。

答案 2 :(得分:0)

这是我使用的方法。您应该能够存储包括错误在内的处理状态。它可以是一列,其值为PENDINGPROCESSEDERROR或两列is_processedis_error

这是为了能够跳过无法成功处理的记录,如果没有跳过则会减慢处理好任务的速度。您可以尝试稍后重新处理它们,或者如果失败的原因是暂时不可用的资源,则可以让DevOps将任务从ERROR移动到PENDING状态。

然后在表上创建条件索引,其中仅包含PENDING个任务。

使用以下算法完成处理(使用spring:transactionnestedTransaction是弹簧事务模板):

while (!(batch = getNextBatch()).isEmpty()):

   transaction.execute( (TransactionStatus status) -> {
         for (Element element : batch) {
                try {
                    nestedTransaction.execute( (TransactionStatuc status ) -> {
                         processElement(element);
                         markAsProcessed(element);
                    });
                } catch (Exception e) {
                    markAsFailed(element);
                } 
         }
    });   

几个重要的注释:

  1. 获取记录是分批完成的 - 这至少可以节省到数据库的往返次数,并且可以更快地逐个检索
  2. 单个元素的处理在嵌套事务中完成(这是使用postgresql SAVEPOINT实现的)。这比处理自己的事务中的每个元素更快,但有一个元素处理失败不会丢失批处理其他元素的结果的好处。
  3. 当处理足够复杂并且无法通过单个查询来处理批处理时,这是很好的。如果processElement更简单地更新element,则可以通过单个更新语句更新整批产品。
  4. 对批次元素的处理可以并行进行。这需要将事务传播到工作线程。