Postgres中的大插入后慢查询

时间:2013-04-21 15:51:59

标签: sql postgresql batch-processing postgresql-9.2 postgresql-performance

我们在RedHat中使用Postgres 9.2。我们有一个类似于以下的表:

CREATE TABLE BULK_WI (
    BULK_ID INTEGER NOT NULL,
    USER_ID VARCHAR(20) NOT NULL,
    CHUNK_ID INTEGER,
    STATE VARCHAR(16),
    CONSTRAINT BASE_BULK_WI_PK PRIMARY KEY(BULK_ID,USER_ID)
);
CREATE INDEX BASE_BULK_WI_IDX01 ON BULK_WI(STATE, CHUNK_ID);

作为批处理作业的一部分,我们首先使用新的BULK_ID向表中添加许多行。所有新记录都有CHUNK_ID = NULL,STATE ='PENDING'。插入物在500K和1.5M行之间。发生这种情况时,表的大小超过15M记录。

插入后,我们开始以块的形式处理表。为此,我们首先为下一个块选择一些项目,然后处理它们。使用以下查询选择项目:

UPDATE BASE_BULK_WI wi SET wi.STATE = 'PROCESSING', wi.CHUNK_ID = $1 
WHERE wi.STATE='PENDING' AND wi.BULK_ID = $2 
AND wi.USER_ID IN 
    (SELECT USER_ID FROM BASE_BULK_WI WHERE BULK_ID = $3 
     AND CHUNK_ID IS NULL AND STATE='PENDING' LIMIT $4 FOR UPDATE)

$ 1 随着每次块重复而增加, $ 2 $ 3 始终相同(刚刚插入的BULK_ID), $ 4 < / strong>通常在2,000到10,000之间。

问题是前几个块需要很长时间才能更新。例如,对于2000的限制,大多数更新发生在1秒以内,而前几个更新需要2分钟。

我们正在努力了解为什么会发生这种情况以及如何解决这个问题。阅读文档后:

  

为确保数据页的一致性,每个检查点之后对数据页的第一次修改会导致记录整个页面内容。

我们认为它与检查站和WAL有关,但我们无法确定它。

有什么建议吗?

2 个答案:

答案 0 :(得分:5)

ANALYZE

autovacuum daemon也会自动运行ANALYZE,但需要一些时间才能启动。如果您在巨大UPDATE之后立即运行INSERT,请确保{ {1}} 介于之间以更新统计信息,或者查询计划程序可能会做出错误的选择。

ANALYZE子句代替FROM

对于大型子查询,

IN的速度非常慢。这可能会表现得更好:

IN

索引

这样的partial index应该是您的最佳选择:

UPDATE base_bulk_wi wi
SET   wi.state = 'PROCESSING'
    , wi.chunk_id = $1 
FROM (
    SELECT user_id, bulk_id 
    FROM   base_bulk_wi
    WHERE  bulk_id = $3 
    AND    chunk_id IS NULL
    AND    state = 'PENDING'
    LIMIT  $4
    FOR    UPDATE
    ) x 
WHERE wi.bulk_id = x.bulk_id
AND   wi.user_id = x.user_id;

为获得最佳性能,请在 CREATE INDEX base_bulk_wi_partial_idx01 ON bulk_wi(chunk_id) WHERE state = 'PENDING' AND chunk_id IS NULL; 后创建此索引。如果它已经存在,则可能有助于之前删除并重新创建。

有人可能认为在此索引中包含INSERT以在Postgres 9.2中允许仅索引扫描是个好主意。但是,由于子查询中有bulk_id,因此无论如何这都不是一个选项。

如果FOR UPDATEuser_id而不是integer,那将会有所帮助。 (用户表的外键。)除了更快的处理和更小的表,两个整数完全适合最小尺寸索引。你的主键会受益很多。

答案 1 :(得分:0)

1)更改condotions的顺序,不确定,但我认为不使用索引:

AND CHUNK_ID IS NULL AND STATE='PENDING'

代表

STATE='PENDING' AND CHUNK_ID IS NULL

WHERE wi.STATE='PENDING' AND wi.BULK_ID = $2

WHERE wi.BULK_ID = $2 AND wi.STATE='PENDING'

2)如果你没有为CHUNK_ID使用列select's,那么我建议添加到索引BASE_BULK_WI_IDX01条件WHERE CHUNK_ID IS NULL - 它将导致索引小得多它仅用于更新。

此外,State列和User_ID列不需要varchar - 您应该使用较小的类型,例如enuminteger。索引将更小,更快地保存和读取数据,将使用更少的磁盘,处理器和其他人员资源。