3

我们在 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 到 150 万行之间。发生这种情况时表的大小超过 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通常在 2,000 到 10,000 之间。

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

我们正试图了解为什么会发生这种情况以及如何解决它。阅读文档后:

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

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

有什么建议么?

4

2 回答 2

5

ANALYZE

autovacuum 守护进程也会自动运行ANALYZE,但需要一些时间才能启动。如果您在一个UPDATE巨大的 .INSERTANALYZE

FROM子句代替IN

IN对于大型子查询,速度非常慢。这可能会表现得更好:

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. 如果它已经存在,它可能有助于在之前删除并在之后重新创建。

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

如果user_id是一个integer而不是varchar. (用户表的外键。)除了更快的处理和更小的表之外,两个整数完全适合最小大小的索引。你的主键会受益不少。

于 2013-04-21T19:32:25.213 回答
0

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

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_IDselect's那么我建议添加到索引BASE_BULK_WI_IDX01条件WHERE CHUNK_ID IS NULL- 它会导致索引更小,因为它仅用于更新。

也不需要 columnStateUser_IDto be varchar- 您应该使用较小的类型,例如enumor integer。索引会小得多,保存和读取数据的速度会更快,使用的磁盘、处理器和其他人员资源也会更少。

于 2013-04-21T17:17:13.047 回答