xpa*_*pad 3 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);
Run Code Online (Sandbox Code Playgroud)
作为批处理作业的一部分,我们首先使用新的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)
Run Code Online (Sandbox Code Playgroud)
每次块迭代增加$ 1,$ 2和$ 3总是相同(刚刚插入BULK_ID),$ 4通常在2,000到10,000之间.
问题是前几个块需要很长时间才能更新.例如,对于2000的限制,大多数更新发生在1秒以内,而前几个更新需要2分钟.
我们试图了解为什么会发生这种情况以及如何解决这个问题.阅读文档后:
为确保数据页的一致性,每个检查点之后对数据页的第一次修改会导致记录整个页面内容.
我们认为这与检查站和WAL有关,但我们无法确定它.
有什么建议?
ANALYZE该自动清理后台程序也运行ANALYZE自动,但它需要一些时间来踢.如果你运行UPDATE后,一个巨大的立即INSERT,确保运行ANALYZE 之间的更新统计,或查询规划可能会做出错误的选择.
FROM 条款而不是 ININ众所周知,大型子查询很慢.这可能会更好:
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;
Run Code Online (Sandbox Code Playgroud)
像这样的部分索引应该是您的最佳选择:
CREATE INDEX base_bulk_wi_partial_idx01 ON bulk_wi(chunk_id)
WHERE state = 'PENDING' AND chunk_id IS NULL;
Run Code Online (Sandbox Code Playgroud)
为获得最佳性能,请在您的后面创建此索引INSERT.如果它已经存在,则可能有助于之前删除并重新创建.
有人可能认为在Postgres 9.2中包含bulk_id这个索引以允许仅索引扫描是个好主意.但是既然你已经FOR UPDATE在子查询中,那么无论如何这都不是一个选择.
它会有所帮助,如果user_id是一个integer而不是varchar.(用户表的外键.)除了更快的处理和更小的表,两个整数完全适合最小尺寸索引.你的主键会受益很多.