性能文章>【译】记一次数据库连接泄漏导致的响应迟缓>

【译】记一次数据库连接泄漏导致的响应迟缓转载

1周前
222005

数据库连接池泄漏其实非常普遍,本文简单记员一次数据库连接池泄漏问题,排查和思考。

问题与分析

问题服务器上的负载激增, Postgres 查询的时间和CPU消耗都异常飙升。

分析:经过调试,得出以下分析:

有一个有 200 行的表,但是显示的活动元组的数量不止这个(大约 60K),我们正在使用的是 Postgresql 9.3。

以下是查询结果:

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)

我还在这张表上尝试了 full vacuum 。结果如下:

  • 一直无法删除行
  • 有几次,所有的活动元组突然变成了死元组。

这是运行 full vacuum 命令的输出:

vacuum FULL VERBOSE ANALYZE subscriber_offset_manager; 
INFO: vacuuming "public.subscriber_offset_manager" 
INFO: "subscriber_offset_manager": found 0 removable, 67920 nonremovable row versions in714 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 and67720 dead rows; 200 rows in sample, 200 estimated total rows VACUUM 


after that i checked for live and dead tuples for that table as follows : 

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

结果所有死元组都移动到活元组,而不是被清理。

有趣的是:当我停止Java 程序,然后做一个full vacuum时,它工作正常(行数和活动元组变得相等)。因此,如果我们从 Java 程序中去进行选择和更新,就会出现问题。

之后对堆栈溢出进行了分析和研究,经过许多尝试,我找到了以下根本原因。

根本原因:

当有一个长时间运行的事务或数据库会话泄漏时,死元组会在该事务的开始时间之后创建,并且无法被该数据库的所有表清理。这因为 PostgreSQL 在执行清理进程前,会检查事务 ID 是否小于最旧事务的事务 ID ,而事务 ID 是全局生成的。

所以当我发现一个交易打开的时间太长时,只要杀死它,vacuum 就能工作正常。

点赞收藏
willberthos

keep foolish!

请先登录,感受更多精彩内容
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

【全网首发】(大表小技巧)有时候 2 小时的 SQL 操作,可能只要 1 分钟

【全网首发】(大表小技巧)有时候 2 小时的 SQL 操作,可能只要 1 分钟

一次java内存top res高排查记录

一次java内存top res高排查记录

干货!Java代码优化必知的30个小技巧!

干货!Java代码优化必知的30个小技巧!

Java 异步调用原理与实战

Java 异步调用原理与实战

没有二十年功力,写不出Thread.sleep(0)这一行“看似无用”的代码!

没有二十年功力,写不出Thread.sleep(0)这一行“看似无用”的代码!

嵌入式linux-CPU性能优化小记

嵌入式linux-CPU性能优化小记

5
0