Postgresql large table 更新变慢

Postgresql large table update slows down

我 运行 一个大 table(例如 8 GB)的更新。它是 table 中 3 个字段的简单更新。我在 postgresql 9.1 下 运行 没有遇到任何问题,这需要 40-60 分钟,但它确实有效。我 运行 9.4 数据库中的相同查询(新创建的,未升级),它开始更新正常,但随后变慢。它仅使用 ~2% CPU,如果 IO 为 4-5MB/s 并且它就在那里,则该级别。没有锁,没有其他查询或连接,只有服务器上的这个单一更新 SQL。

下面是SQL。 "lookup" table 有 12 条记录。查找只能 return 一行,它将离散比例 (SMALLINT, -32768 .. +32767) 分成非重叠区域。 "src" 和 "dest" table 大约有 6000 万条记录。

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 的 VM媒体)。所有其他磁盘操作不受影响,有足够的 IO 带宽。同时 PostgreSQL 只是坐在那里做的很少,运行 非常缓慢。

我有 2 台虚拟化 linux 服务器,一台 运行s postgresql 9.1 和另一台 运行s 9.4。两台服务器的 postgresql 配置几乎相同。

有没有人有过类似的经历?我 运行 没主意了。帮助。

编辑 查询 "ran" 20 小时,我不得不终止连接并重新启动服务器。令人惊讶的是它没有通过查询终止连接:

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

并且服务器产生了以下日志:

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.

"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" - 这是否表示 PostgreSQL 中存在错误?

编辑 我测试了 9.1、9.3 和 9.4。 9.1 和 9.3 都没有遇到速度下降的情况。 9.4 持续减慢大型交易。我注意到,当事务启动时,htop 监视器指示高 CPU,进程状态为 "R"(运行ning)。然后它逐渐变为低 CPU 使用率和状态 "D" - 磁盘(参见屏幕截图 )。我最大的问题是为什么 9.4 与 9.1 和 9.3 不同?我有十几台服务器,这种影响是全面观察到的。

我怀疑有很多磁盘搜索 - 5MB/s 对于普通(旋转)硬盘驱动器上非常随机的 IO 来说刚刚好。

当你不断替换基本上所有的行时,我会尝试将 dest table 填充因子设置为大约 45% (alter table dest set (fillfactor=45);),然后 cluster test using test_pkey;。这将允许将更新的行版本放置在同一磁盘扇区中。

另外使用 cluster src using src_pkey; 这样两个 table 都可以在磁盘上以相同的物理顺序存储数据也有帮助。

还记得 vacuum table dest; 每次更新那么大之后,旧行版本可以在后续更新中再次使用。

您的旧服务器可能 进化 它在多次更新期间自然 fillfactor。在新服务器上它是 100% 打包的,所以更新的行必须放在最后。

如果实际更新的目标行很少,可以使用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
        )
        ;

感谢大家的帮助。无论我多么努力地强调 9.4 和以前版本的相同配置之间的性能差异,似乎都没有人注意这一点。

问题已通过禁用透明大页面解决:

echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

以下是我发现有助于研究问题的一些资源:
* 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