对具有 GIN 索引的 PostgreSQL 表的偶尔/间歇性、缓慢(10+ 秒)更新查询

Chr*_* W. 2 postgresql performance index gin-index postgresql-performance

设置

我在基于 SSD 的四核虚拟专用服务器 (VPS) 和 Debian Linux (8) 上运行 PostgreSQL 9.4.15。相关表有大约 200 万条记录。

记录经常被插入,甚至更频繁地(不断——至少每隔几秒钟)更新一次。据我所知,我已经为这些操作准备了所有适当的索引,以便快速执行,而且绝大多数时间它们确实会立即执行(以毫秒为单位)。

问题

然而,每隔一小时左右,其中一个UPDATE查询就会花费过多的时间——比如 10 秒或更长时间。当这种情况发生时,它通常就像“一批”被“阻塞”的查询,几乎同时终止。就好像其中一个查询或其他一些后台操作(例如,真空)正在阻止它们。

架构

表 ,items有很多列,但我认为以下是唯一可能与问题相关的列:

  • id INTEGER NOT NULL (首要的关键)
  • search_vector TSVECTOR
  • last_checkup_at TIMESTAMP WITHOUT TIME ZONE

这些是相关的索引:

  • items_pkey PRIMARY KEY, btree (id)
  • items_search_vector_idx gin (search_vector)
  • items_last_checkup_at_idx btree (last_checkup_at)

可能的罪魁祸首

最后,当pg_stat_activity我的日志文件中发出“连接泄漏”警告时,在组装了一个小脚本以转储(所有活动 Postgres 连接/查询的列表)的内容后,我缩小了可能的罪魁祸首查询/列(假设问题不是外部的,比如行为不端的 VPS)。粗略地说,这些查询似乎一次又一次地出现:

  • UPDATE items SET last_checkup_at = $1 WHERE items.id = 123245
  • UPDATE items SET search_vector = [..] WHERE items.id = 78901

这些略有解释,但我真的怀疑缺少任何相关内容。偶尔也会出现其他查询(在其他表上),但这些查询通常看起来只是“不走运”而被卷入其中。

现在,即使第一个查询(设置last_checkup_at)往往出现在大多数时间,设置的查询search_vector似乎每次都会出现。(此外,一般来说,第一个查询可能有更多的实例被发出,这使得它更有可能只是偶然出现。)

(我想我在这里寻找解决方案,但即使我把它放在包里,我也想为其他人记录这里的事件......几个月来我一直对这个问题感到困惑,然后才有机会深潜。)

Chr*_* W. 6

问题似乎出在 Postgres 的“ FASTUPDATE”机制上。

FASTUPDATEGIN索引可用设置,启用后,会导致对索引的更改(由UPDATEs引起,并且可能由s引起INSERT)“排队”。然后,一旦这个“队列”变得太大,挂起的条目就会正确地集成到GIN索引中。

目的FASTUPDATE是(毫不奇怪)加快索引更新,但不幸的是,它会导致偶尔的 UPDATE查询异常缓慢。就我而言,我发现最好预先进行命中(主要是为了避免在我的日志中出现“慢查询”的警告)。

FASTUPDATE显然默认启用并且自 PostgreSQL 8.4 起可用。我能够像这样禁用它:

ALTER INDEX items_search_vector_idx SET (FASTUPDATE=OFF);
Run Code Online (Sandbox Code Playgroud)

在撰写本文时,我已经运行了将近一个星期,几乎没有慢查询。(除了一个我预计需要很长时间的查询,我几乎没有注意到其他问题。)

您还可以在 Postgres 邮件列表的相关主题中找到更多相关信息。有趣的是,Postgres 开发人员之一(Tom Lane)建议处理FASTUPDATE待处理项目“不应该阻止并发插入”,但我不确定这是否正确;在我的情况下,我会看到几个查询被“备份”,然后一次完成。