postgresql/Vacuum中的大量活/死元组无法正常工作

Sah*_*wal 14 java postgresql performance hibernate postgresql-9.3

有一张桌子,有200行.但是显示那里的实时元组的数量超过了(大约60K).

select count(*) from subscriber_offset_manager;
 count 
-------
   200
(1 row)


 SELECT schemaname,relname,n_live_tup,n_dead_tup FROM pg_stat_user_tables  where relname='subscriber_offset_manager' ORDER BY n_dead_tup
;
 schemaname |          relname          | n_live_tup | n_dead_tup 
------------+---------------------------+------------+------------
 public     | subscriber_offset_manager |      61453 |          5
(1 row)
Run Code Online (Sandbox Code Playgroud)

但是从pg_stat_activity和pg_locks可以看出,我们无法跟踪任何打开的连接.

SELECT query, state,locktype,mode
FROM pg_locks
JOIN pg_stat_activity
  USING (pid)
WHERE relation::regclass = 'subscriber_offset_manager'::regclass
  ;
 query | state | locktype | mode 
-------+-------+----------+------
(0 rows)
Run Code Online (Sandbox Code Playgroud)

我也试过这张桌子上的全真空,下面是结果:

  • 始终没有删除任何行
  • 有时候所有的活元组都会变成死元组.

这是输出.

vacuum FULL VERBOSE ANALYZE subscriber_offset_manager;
INFO:  vacuuming "public.subscriber_offset_manager"
INFO:  "subscriber_offset_manager": found 0 removable, 67920 nonremovable row versions in 714 pages
DETAIL:  67720 dead row versions cannot be removed yet.
CPU 0.01s/0.06u sec elapsed 0.13 sec.
INFO:  analyzing "public.subscriber_offset_manager"
INFO:  "subscriber_offset_manager": scanned 710 of 710 pages, containing 200 live rows and 67720 dead rows; 200 rows in sample, 200 estimated total rows
VACUUM

 SELECT schemaname,relname,n_live_tup,n_dead_tup FROM pg_stat_user_tables  where relname='subscriber_offset_manager' ORDER BY n_dead_tup
;
 schemaname |          relname          | n_live_tup | n_dead_tup 
------------+---------------------------+------------+------------
 public     | subscriber_offset_manager |        200 |      67749
Run Code Online (Sandbox Code Playgroud)

10秒后

SELECT schemaname,relname,n_live_tup,n_dead_tup FROM pg_stat_user_tables  where relname='subscriber_offset_manager' ORDER BY n_dead_tup
;
 schemaname |          relname          | n_live_tup | n_dead_tup 
------------+---------------------------+------------+------------
 public     | subscriber_offset_manager |      68325 |        132
Run Code Online (Sandbox Code Playgroud)

我们的App如何查询此表.

  • 我们的应用程序通常选择一些行并根据一些业务计算更新行.

    选择查询 - 根据某些ID选择

    select*from subscriber_offset_manager,其中shard_id = 1;

    更新查询 - 更新此选定分片ID的其他一些列

  • 大约20个线程并行执行此操作,一个线程仅在一行上工作.

  • app在java中编写,我们正在使用hibernate来执行db操作.
  • Postgresql版本是9.3.24

另一个有趣的观察: - 当我停止我的Java应用程序,然后完全真空,它工作正常(行数和实时元组变得相等).因此,如果我们从Java应用程序中连续选择和更新,则会出现问题.-

问题/发行

这些活元组有时会去死元组,并且经过一段时间再次生存.

由于上述行为,从表中选择花费时间和增加服务器上的负载,因为有很多活/死的..

Erw*_*ter 5

毕竟可能存在锁,您的查询可能会产生误导:

\n\n
SELECT query, state,locktype,mode\nFROM pg_locks\nJOIN pg_stat_activity USING (pid)\nWHERE relation = \'subscriber_offset_manager\'::regclass\n
Run Code Online (Sandbox Code Playgroud)\n\n

pg_locks.pid可以为 NULL,那么连接将消除行。Postgres 9.3 的手册:

\n\n
\n

持有或等待此锁的服务器进程的进程 ID,如果该锁由准备好的事务持有,则为 null

\n
\n\n

大胆强调我的。(第 10 页仍然相同。)

\n\n

通过简单的查询你能得到什么吗?

\n\n
SELECT * FROM pg_locks\nWHERE relation = \'subscriber_offset_manager\'::regclass;\n
Run Code Online (Sandbox Code Playgroud)\n\n

这可以解释为什么VACUUM抱怨:

\n\n
\n
DETAIL:  67720 dead row versions cannot be removed yet.\n
Run Code Online (Sandbox Code Playgroud)\n
\n\n

反过来,这会指出应用程序逻辑/查询中的问题,锁定比必要的更多的行。

\n\n

我的第一个想法是长时间运行的事务,即使是一个简单的事务SELECT(获取低级ACCESS SHARE锁)也可能阻止VACUUM其工作。20 个并行线程可能会连锁并锁定VACUUM。让您的事务(及其锁)尽可能简短。并确保您的查询已优化并且不会锁定不必要的行。

\n\n

还有一件事需要注意:事务隔离级别SERIALIZABLEREPEATABLE READ使VACUUM清理变得更加困难。默认READ COMMITTED模式限制较少,但是VACUUM仍然可以按照讨论的方式进行阻止。

\n\n

有关的:

\n\n\n


Lau*_*lbe 5

我知道三件事情VACUUM无法完成它的工作:

  • 长时间运行的事务。

  • 未提交的已准备事务。

  • 陈旧的复制槽。

有关详细信息,请参阅我的博客文章


Sah*_*wal 5

我有问题吗?。

为了理解该问题,请考虑以下流程:

线程1-

  • 打开休眠会话
  • 表A上进行一些查询
  • subscriber_offset_manager中选择
  • 更新subscription_offset_manager
  • 关闭会话。

并行运行的Thread-1类型的许多线程。

线程2-

  • 这些类型的线程并行运行。
  • 打开休眠会话
  • 表A上进行一些选择查询
  • 不关闭会话。(会话泄漏。)

临时解决方案 -如果我使用pg_cancel_backend关闭了Thread-2进行的所有连接,则清理工作开始。

此外,我们已经多次重现该问题,并尝试了此解决方案,并且该解决方案有效。

现在,有以下疑问仍未得到解决。

  1. 为什么postgres没有显示与表“ subscriber_offset_manager ” 有关的任何数据。
  2. 当我们使用psql 在表A上运行select 时,而不是运行Thread-2时不会重新创建此问题。
  3. 为什么postgres与jdbc像这样工作。

一些更令人惊讶的观察:

  1. 如果我们在不同的会话上对“ subscriber_offset_manager ”进行查询的话,也会发出来;
  2. 我们在这里发现许多实例,其中线程2正在第三个表“ Table-C ”上工作,问题来了
  3. pg_stat_activity中所有这些类型的od事务状态都是“ idle_in_transaction”

@Erwin Brandstetter和@Laurenz Albe,如果您知道存在与postgres / jdbc相关的错误。