我们在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有关,但我们无法确定它。
有什么建议吗?
答案 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 UPDATE
是user_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
- 您应该使用较小的类型,例如enum
或integer
。索引将更小,更快地保存和读取数据,将使用更少的磁盘,处理器和其他人员资源。