我试图对包含6个表的大型数据库应用125个不同的更新,这些表在每个表中具有100k记录到3亿个记录的范围。
每个更新都包含要插入到原始6个表中的新数据,但是更新还包含将成为原始表中已存在的记录的下一个版本的数据。如果是这种情况,那么我需要使用更新加载号更新字段。更新数据和原始数据包含唯一的id,它是20个字符的varchar,在原始表和更新表上都有标准的BTree索引。
原始数据的一个例子是这个
unique_id, version, version_date, change_dates,"tlzb1000001554000601";7;"2003-12-22";"{1995-12-04,1995-12-14,2002-06-21,2002-06-25,2003-12-16}"
并且更新记录将是
unique_id, version, version_date, change_dates,"tlzb1000001554000601";8;"2004-08-10";"{1995-12-04,1995-12-14,2002-06-21,2002-06-25,2003-12-16,2004-07-27}"
由于我需要跟踪哪个更新号影响了记录,我在原始数据表中添加了一个update_number,如果有一个匹配的unique_id的记录,我希望更新。
因此,对于每次更新,我一直在将数据加载到一组与原始数据的模式匹配的6个表中,然后应用更新以便任何正在更新的记录我将更新的整数字段设置为我正在处理的更新数字。
UPDATE original_table
SET load_number = ${update_number}
WHERE unique_id IN (SELECT unique_id FROM update_table)
这不能很好地工作,每次更新通常需要10个小时。经过一些研究,我发现这个advice,所以改变了我的查询,使用CTE和'FROM'
WITH new AS (
SELECT unique_id
FROM update_table
)
UPDATE original_table o
SET load_number = ${update_number}
FROM new n
WHERE o.unique_id=n.unique_id
使用上述查询,我设法在一周内全天候运行32次更新。我试图通过暂时关闭表的auto_vacuum来加快速度。
我还尝试将数据删除加载到临时表中,然后将其插入更新的字段中。
WITH new AS (
SELECT unique_id FROM update_table
), tmp AS (
DELETE FROM original_table b
USING new n
WHERE b.unique_id=n.unique_id
RETURNING *)
INSERT INTO old_data SELECT * FROM tmp
然而,这似乎需要4倍的时间。
所以我现在已经筋疲力尽了我能想到的所有变化,所以我可以尝试一些替代方案。
我想到但不确定如何实现的一个可能选项是将所有更新数据加载到6个更新表中,并将load_number字段设置为更新编号。完成所有125次更新后,我将使用这些表来修改原始表。但不知道如何以正确的顺序更新记录并将load_number设置为正确的记录。
希望有人有解决方案,提前谢谢
额外信息: - 我在Windows 64位服务器上有一个PostgreSQL 9.6数据库,有20个核心和128Gb RAM。我根据wiki调优建议调整了数据库。
对我来说,似乎你正在尝试做一些相当于:
INSERT INTO original_table
SELECT * FROM update_table
ON CONFLICT (unique_id) DO UPDATE SET
load_number = ${update_number},
version = EXCLUDED.version,
version_date = EXCLUDED.version_date,
change_dates = EXCLUDED.change_dates