一次數(shù)據(jù)庫連接泄漏案例的排查和思考

本文來自于HeapDump性能社區(qū)! !有性能問題,上HeapDump性能社區(qū)!
數(shù)據(jù)庫連接池泄漏其實(shí)非常普遍,本文簡(jiǎn)單記員一次數(shù)據(jù)庫連接池泄漏問題,排查和思考。

問題與分析

問題:服務(wù)器上的負(fù)載激增, Postgres 查詢的時(shí)間和CPU消耗都異常飆升。
分析:經(jīng)過調(diào)試,得出以下分析:
有一個(gè)有 200 行的表,但是顯示的活動(dòng)元組的數(shù)量不止這個(gè)(大約 60K),我們正在使用的是 Postgresql 9.3。

以下是查詢結(jié)果:

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 。結(jié)果如下:

一直無法刪除行
有幾次,所有的活動(dòng)元組突然變成了死元組。
這是運(yùn)行 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

結(jié)果所有死元組都移動(dòng)到活元組,而不是被清理。

有趣的是:當(dāng)我停止Java 程序,然后做一個(gè)full vacuum時(shí),它工作正常(行數(shù)和活動(dòng)元組變得相等)。因此,如果我們從 Java 程序中去進(jìn)行選擇和更新,就會(huì)出現(xiàn)問題。

之后對(duì)堆棧溢出進(jìn)行了分析和研究,經(jīng)過許多嘗試,我找到了以下根本原因。

根本原因:

當(dāng)有一個(gè)長(zhǎng)時(shí)間運(yùn)行的事務(wù)或數(shù)據(jù)庫會(huì)話泄漏時(shí),死元組會(huì)在該事務(wù)的開始時(shí)間之后創(chuàng)建,并且無法被該數(shù)據(jù)庫的所有表清理。這因?yàn)?PostgreSQL 在執(zhí)行清理進(jìn)程前,會(huì)檢查事務(wù) ID 是否小于最舊事務(wù)的事務(wù) ID ,而事務(wù) ID 是全局生成的。

所以當(dāng)我發(fā)現(xiàn)一個(gè)交易打開的時(shí)間太長(zhǎng)時(shí),只要?dú)⑺浪?,vacuum 就能工作正常。
更多數(shù)據(jù)庫性能案例:

從一次數(shù)據(jù)庫問題排查實(shí)戰(zhàn)

5G時(shí)代,如何徹底搞定海量數(shù)據(jù)庫的設(shè)計(jì)與實(shí)踐

數(shù)據(jù)庫系列1:高并發(fā)下的數(shù)據(jù)字段變更

Prometheus時(shí)序數(shù)據(jù)庫-數(shù)據(jù)的查詢

Mysql數(shù)據(jù)庫查詢好慢,除了索引,還能因?yàn)槭裁矗?/a>

數(shù)據(jù)庫系列:MySQL索引優(yōu)化與性能提升總結(jié)(綜合版)

帶你遨游銀河系的 10 種分布式數(shù)據(jù)庫

得物關(guān)于Redis緩存一致性問題的優(yōu)化和實(shí)踐

Mysql的sql優(yōu)化方法

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容