有一个表,其中有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
我们的应用程序如何查询此表。
我们的应用程序通常选择一些行,并根据一些业务计算来更新该行。
选择查询-根据某些ID进行选择
从subscriber_offset_manager中选择*,其中shard_id = 1;
更新查询-为此选定的分片ID更新其他一些列
大约20个线程并行执行此操作,而一个线程仅在一行上工作。
另一个有趣的发现: -当我停止我的Java应用程序,然后完全吸尘时,它工作正常(行数和活动元组变为相等)。因此,如果我们从Java应用程序中连续选择并更新,则会出问题。 –
问题/问题
这些活的元组有时会变成死的元组,过一段时间后又会复活。
由于上述行为,请从表中进行选择,这会花费一些时间并增加服务器的负载,因为那里有很多实时/重复数据..
答案 0 :(得分:4)
我遇到了问题。
要了解该问题,请考虑以下流程:
线程1-
许多类型为 Thread-1 的线程并行运行。
线程2-
临时解决方案-如果我使用pg_cancel_backend关闭了Thread-2建立的所有连接,则清理工作开始。
我们还多次创建了该问题,并尝试了此解决方案,并且该解决方案有效。
现在,有以下疑问仍未得到解答。
更多令人震惊的观察结果:
@Erwin Brandstetter和@Laurenz Albe,如果您知道存在与postgres / jdbc相关的错误。
答案 1 :(得分:3)
答案 2 :(得分:2)
毕竟可能有锁,您的查询可能会引起误解:
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级别SERIALIZABLE
或REPEATABLE READ
使得VACUUM
清理变得更加困难。默认的READ COMMITTED
模式的限制较少,但仍可以按照讨论阻止VACUUM
。
相关: