【问题标题】:High number of live/dead tuples in postgresql/ Vacuum not workingpostgresql/真空中大量的活/死元组不起作用
【发布时间】:2018-08-14 15:09:49
【问题描述】:

有一个表,有 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)

但是从 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)

我也在这张桌子上尝试了全真空,以下是结果:

  • 所有时间都没有删除行
  • 有时所有的活动元组都变成了死元组。

这是输出。

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

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

我们的 App 是如何查询这个表的。

  • 我们的应用程序一般会选择一些行,并根据一些业务计算,更新行。

    选择查询 -- 根据某个 id 选择

    select * from subscriber_offset_manager where shard_id=1 ;

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

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

  • app 是用 java 编写的,我们使用 hibernate 来进行数据库操作。
  • Postgresql 版本为 9.3.24

另一个有趣的观察结果: - 当我停止我的 java 应用程序然后完全真空时,它工作正常(行数和活动元组变得相等)。因此,如果我们从 java app 中不断选择和更新,就会出现问题。 -

问题/问题

这些活的元组有时会变成死元组,然后又会活过来。

由于上述行为,从表中选择需要时间并增加服务器上的负载,因为那里有很多 live/deadtuples ..

【问题讨论】:

  • 听起来好像有什么严重的错误。 Postgres 9.3 哪一点发布?最新的 9.3.23? SHOW track_counts 能得到什么?
  • Postgres 版本是 9.3.24 。再观察一下 - 当我停止我的 java 应用程序然后进行全真空时,它工作正常。所以如果我们不断选择和更新就会有问题。
  • 您可能会显示用于选择/更新行的查询。
  • 添加了有问题的:选择查询-基于一些id选择*从订阅者偏移管理器那里shard_id=1;更新查询 -- 更新此选定分片 ID 的其他列

标签: java postgresql performance hibernate postgresql-9.3


【解决方案1】:

我知道阻止VACUUM 工作的三件事:

  • 长时间运行的事务。

  • 未提交的已准备事务。

  • 过时的复制槽。

详情请见my blog post

【讨论】:

  • 我尝试了所有三件事,但返回零行,没有锁,没有准备好的事务,没有复制槽 ....
  • 我得到了问题并发布了答案,但我仍然有疑问,我在答案中提到了,请检查您是否可以回答。谢谢。
  • 我无法应用您的第一点,因为我使用的是 postgres 9.3 并且 backend_xmin 不存在。所以,我选择了所有,没有发现任何长期运行的事务。所以.结论是当有正在运行的事务时,之后创建的死元组将不会被所有表的真空清理,因为事务ID是全局生成的,它检查的事务ID小于最旧事务的事务ID.Thanx .
  • 很抱歉我的查询无效。但是在pg_stat_activity 中很容易找到长事务。很高兴你能解决这个问题。
  • 写了一篇关于会话泄漏可以做什么的博客,它也会回答上面的问题hello-worlds.in/2021/03/28/…
【解决方案2】:

我有问题☺。

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

线程 1 -

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

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

线程 2 -

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

临时解决方案 - 如果我使用 pg_cancel_backend 关闭 Thread-2 建立的所有连接,则吸尘开始工作。

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

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

  1. 为什么 postgres 没有显示与表“subscriber_offset_manager”相关的任何数据。
  2. 如果我们使用 psql 在 Table-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 相关的错误。

【讨论】:

  • 我知道根本原因。因此,结论是当有一个正在运行的事务时,之后创建的死元组将不会被所有表的真空清理,因为事务 id 是全局生成的,它检查的事务 id 小于最旧事务的事务 id。
【解决方案3】:

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

SELECT query, state,locktype,mode
FROM pg_locks
JOIN pg_stat_activity USING (pid)
WHERE relation = 'subscriber_offset_manager'::regclass

pg_locks.pid 可以为 NULL,则连接将消除行。 The manual for Postgres 9.3:

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

我的大胆强调。 (第 10 页还是一样。)

对于这个简单的查询,你有什么收获吗?

SELECT * FROM pg_locks
WHERE relation = 'subscriber_offset_manager'::regclass;

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

DETAIL:  67720 dead row versions cannot be removed yet.

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

我的第一个想法是长时间运行的事务,即使是简单的SELECT(获取低级ACCESS SHARE 锁)也可以阻止VACUUM 完成其工作。 20 个并行线程可能会无限期地链接并锁定 VACUUM。使您的事务(及其锁定)尽可能简短。并确保您的查询已经过优化,并且不会锁定过多的行。

还有一点需要注意:transaction isolation 级别 SERIALIZABLEREPEATABLE READ 使 VACUUM 更难清理。默认的READ COMMITTED 模式限制较少,但VACUUM 仍可如所讨论的那样被阻止。

相关:

【讨论】:

  • Ran 您建议的查询:死元组大约是 80k,计数为 200,锁没有给出任何信息 SELECT * FROM pg_locks WHERE relationship = 'subscriber_offset_manager'::regclass; 0 行返回
  • 关键是VACUUM试图清理的那一刻没有锁。
  • 是的,这种情况正在发生,但不知道为什么?
  • 您必须学习 Java 应用程序的事务处理和查询。交易开放时间过长? 20 个并行线程可能会无限期地链接并锁定 VACUUM。使事务(及其锁)尽可能简短。
  • 我又添加了一个关于事务隔离级别的提示。
猜你喜欢
  • 2019-01-21
  • 1970-01-01
  • 2020-05-04
  • 2018-09-16
  • 2014-11-07
  • 1970-01-01
  • 1970-01-01
  • 2020-03-30
  • 1970-01-01
相关资源
最近更新 更多