[PostgreSQL] Connection Pool 이 꽉 찼을 때 대처법 ( feat. lock )

식빵·2023년 5월 18일
0

postgresql-memo

목록 보기
20/34
post-thumbnail

오늘 일하는 곳에서 갑자기 DB Connection 이 안 잡히는 현상이 발생했습니다.
에러 로그를 보니 아래와 같은 로그가 남아있더군요.

org.postgresql.util.PSQLException: FATAL: sorry, too many clients already.

로그에 쓰인 말 그대로,
PostgreSQL DBMS 의 최대 Connection 의 수에 도달했는데, 또 다시 Connection 이
요청되서 에러가 난 겁니다. 대체적으로 이런 상황을 해결하는 방법은 크게 2가지입니다.

  1. Max Connection 의 수의 늘린다.
  2. 연결된 Connection/Session 을 강제로 끊어버린다.

자, 여러분들은 어떤 방법을 선택하시겠습니까?
아마 바로 판단하기 힘들겁니다. 최종적인 판단을 위해서는 일단
현재 Connection Pool 이 꽉 찬 이유가 뭔지, 즉 현재 상황에 대한 파악이 먼접니다.



상황파악


먼저 Connection Pool 이 꽉 차면 아래 2개의 System table 을 통해서
다음의 내용을 파악해야 합니다.

  • pg_settings : 현재 최대 연결 가능한 세션 수는 몇인가?
  • pg_stat_activity : 현재 어떤 세션들이 Connection 을 물고 있는가?

위처럼 먼저 상황을 파악하고 DB Connection 을 강제로 죽이든,
최대세션을 늘리든 결정해야 합니다.

이 글에서는 DB Connection 을 죽이는 방법을 기록합니다.
최대세션을 늘리는 방법은 이 링크를 참조하시기 바랍니다.


Max Connection 갯수 확인

select * from pg_settings
where name = 'max_connections';



pg_stat_activity, 세션 파악하기

SELECT * FROM pg_stat_activity;

pg_stat_activity 를 조회하면 위그림 처럼 현재 어떤 client ip 에서 세션을
사용 중인지 확인을 할 수 있습니다. 추가적으로 pg_stat_activity.query 컬럼을
통해서 각 세션별로 어떤 query 를 수행했는지도 확인 가능합니다. (아래 그림 참고)
이런 정보가 있으면 좀 더 상황 파악하기가 좋겠죠?

어느정도 상황이 파악되면 DB 연결을 끊고 싶은 Session 과 실제 연결된
DB Server Process pid 를 지정합니다.
pg_stat_activity.pid 컬럼을 참조하면 됩니다. (아래 그림 참고)

Lock 또는 Blocking 을 유발하는 PID 를 정확히 뽑아내려면 아래 쿼리를 돌리고...

select pid,
       usename,
       application_name,
       pg_blocking_pids(pid) as blocked_by,
       query as blocked_query,
       backend_start,
       query_start,
       backend_type
from pg_stat_activity
where cardinality(pg_blocking_pids(pid)) > 0;

blocked_by 에 나오는 숫자값으로 blocking 을 유도하는 pid 를 뽑아낼 수 있습니다.



session 끊기


위에서 선택한 pid 를 아래 쿼리에 넣어서 돌리면 강제로 해당
connection 을 끊을 수 있습니다.

SELECT 
	pg_cancel_backend(pid), -- 해당 PID만 중지
	pg_terminate_backend(pid) -- PID와 연계된 모든 상위 쿼리 프로세스도 종료
FROM pg_stat_activity 
where 
pid <> pg_backend_pid() -- (필수) 현재 이 쿼리르 돌리는 session 의 pid 는 끊지 않음
and backend_type = 'client backend' -- (필수) 클라이언트 전용 session 만 끊기, DB 내부 사용 세션은 X
-- and pid in (1826,5123,5132,61112)  -- (선택) 특정 session 만 끊기
-- and datname = 'fantastic_db'; -- (선택) 특정 DB 접속 session 만 끊기



참고 링크

profile
백엔드를 계속 배우고 있는 개발자입니다 😊

0개의 댓글