Postgresql大表更新速度变慢

时间:2015-05-21 15:22:41

标签: postgresql postgresql-9.4

我在一个大表(例如8 GB)上运行更新。它是表格中3个字段的简单更新。我在postgresql 9.1下运行它没有任何问题,它需要40-60分钟,但它工作。我在9.4数据库(刚创建,未升级)中运行相同的查询,它启动更新正常,但然后减慢。它只使用~2%的CPU,如果IO为4-5MB / s并且它就坐在那里。没有锁,没有其他查询或连接,只是服务器上的这个单一更新SQL。

SQL如下。 "查找"表有12条记录。查找只能返回一行,它会将离散比例(SMALLINT,-32768 .. + 32767)分解为非重叠区域。 " SRC"和" dest"这些表约有6千万条记录。

UPDATE dest SET
    field1 = src.field1,
    field2 = src.field2,
    field3_id = (SELECT lookup.id FROM lookup WHERE src.value BETWEEN lookup.min AND lookup.max)
FROM src
WHERE dest.id = src.id;

我认为我的磁盘速度变慢但我可以并行复制1 GB文件以执行查询,并且它以> 40MB / s的速度快速运行,而且我只有一个磁盘(它是一个带有ISCSI媒体的虚拟机)。所有其他磁盘操作都不受影响,有足够的IO带宽。与此同时,PostgreSQL只是坐在那里做得很少,跑得很慢。

我有2个虚拟化的linux服务器,一个运行postgresql 9.1,另一个运行9.4。两个服务器都具有接近相同的postgresql配置。

还有其他人有类似的经历吗?我的想法已经不多了。帮助

修改 查询"运行" 20个小时我不得不杀死连接并重新启动服务器。令人惊讶的是,它并没有通过查询来终止连接:

SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE pid <> pg_backend_pid() AND datname = current_database();

和sever产生了以下日志:

2015-05-21 12:41:53.412 EDT FATAL:  terminating connection due to administrator command
2015-05-21 12:41:53.438 EDT FATAL:  terminating connection due to administrator command
2015-05-21 12:41:53.438 EDT STATEMENT:  UPDATE <... this is 60,000,000 record table update statement>

服务器重启也需要很长时间,产生以下日志:

2015-05-21 12:43:36.730 EDT LOG:  received fast shutdown request
2015-05-21 12:43:36.730 EDT LOG:  aborting any active transactions
2015-05-21 12:43:36.730 EDT FATAL:  terminating connection due to administrator command
2015-05-21 12:43:36.734 EDT FATAL:  terminating connection due to administrator command
2015-05-21 12:43:36.747 EDT LOG:  autovacuum launcher shutting down
2015-05-21 12:44:36.801 EDT LOG:  received immediate shutdown request
2015-05-21 12:44:36.815 EDT WARNING:  terminating connection because of crash of another server process
2015-05-21 12:44:36.815 EDT DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.

&#34; postmaster已命令此服务器进程回滚当前事务并退出,因为另一个服务器进程异常退出并且可能已损坏的共享内存&#34; - 这是PostgreSQL中错误的指示吗?

修改 我测试了9.1,9.3和9.4。 9.1和9.3都不会经历减速。 9.4始终减慢大型交易的速度。我注意到当一个事务开始时,htop monitor指示高CPU并且进程状态是&#34; R&#34; (运行)。然后它逐渐变为低CPU使用率和状态&#34; D&#34; - disk(见截图Disk waiting)。我最大的问题是为什么9.4不同于9.1和9.3?我有十几个服务器,这种效果是全面的。

3 个答案:

答案 0 :(得分:4)

感谢大家的帮助。无论我多么努力强调echo never > /sys/kernel/mm/transparent_hugepage/enabled echo never > /sys/kernel/mm/transparent_hugepage/defrag 和先前版本的相同配置之间的性能差异,似乎没有人注意到这一点。

通过禁用透明的大页面解决了这个问题:

hide()

我发现以下资源有助于研究问题:
* https://dba.stackexchange.com/questions/32890/postgresql-pg-stat-activity-shows-commit/34169#34169
* https://lwn.net/Articles/591723/
* https://blogs.oracle.com/linux/entry/performance_issues_with_transparent_huge

答案 1 :(得分:0)

我怀疑要寻找大量磁盘 - 对于普通(旋转)硬盘上的非常随机的IO,5MB / s是正确的。

当您不断更换所有行时,我会尝试将dest表fillfactor设置为约45%(alter table dest set (fillfactor=45);)然后cluster test using test_pkey;。这样可以将更新的行版本放在同一磁盘扇区中。

另外使用cluster src using src_pkey;因此两个表在磁盘上具有相同物理顺序的数据也可以提供帮助。

另请记住,每次更新后vacuum table dest;都会在后续更新中再次使用较大的旧行版本。

您的旧服务器可能会在多次更新过程中自然地进化 fillfactor 。在新服务器上,它被打包100%,因此必须在最后放置更新的行。

答案 2 :(得分:0)

如果实际只更新了几个目标行,则可以避免使用DISTICNT FROM生成新的行版本。这可以防止大量无用的磁盘流量。

UPDATE dest SET
    field1 = src.field1,
    field2 = src.field2,
    field3_id = lu.id
FROM src
JOIN lookup lu ON src.value BETWEEN lu.min AND lu.max
WHERE dest.id = src.id
        -- avoid unnecessary row versions to be generated
AND     (dest.field1 IS DISTINCT FROM src.field1
        OR dest.field1 IS DISTINCT FROM src.field1
        OR dest.field3_id IS DISTINCT FROM lu.id
        )
        ;