티스토리 뷰

발생일: 2018.08.28

키워드: max_execution_time, slow query, cpu 100% of database

문제:

실수로 실행 시간이 아주 긴 쿼리가 운영 중인 서비스에서 실행됐다.
서버에서 맺은 커넥션은 타임아웃이 걸려있어서 문제 없이 끊겼는데, MySQL 서버의 CPU는 여전히 100%다.

디비 서버의 모든 커넥션을 끊은 후에도 한동안 CPU가 100%로 유지되더라.
문제가 뭘까.


해결책:

문제의 쿼리는 SELECT 구문으로 무려 80초나 걸리는 것이었는데, 커넥션이 끊긴 이후에도 해당 쿼리는 계속 실행된 게 원인으로 보인다.
동일 쿼리가 여러 번 실행됐을 거고, 이런 이유로 CPU가 계속 100%가 되었던 것으로 추측된다.

커넥션 타임아웃(connet_timeout)을 설정해둬서 이런 경우에도 문제 없을 거라고 생각했고,
언뜻 커넥션이 끊겼으니 문제의 쿼리도 종료됐을 거라 생각했는데 아니었다.

커넥션 타임아웃을 설정하는 것과 쿼리의 실행 시간을 제한하는 것은 별개의 문제다.
또한, 운영 중인 디비 서버에서 실행 시간이 지나치게 긴 쿼리는 제한 시간 내에 종료되어야 하는 게 정상이다.

MySQL에선 max_execution_time 을 설정해 SELECT 구문의 실행 시간을 제한할 수 있다.

서비스 뿐만 아니라, 배치 작업 등 여러 환경에서 디비 서버를 사용하고 있기 때문에 글로벌로 설정하는 대신 세션 단위로 설정하기로 했다.
커넥션 풀에서 커넥션을 맺을 때 아래 쿼리를 실행해 시간을 제한했다. 단위는 밀리세컨드이다.


    SET SESSION max_execution_time = 5000  -- SELECT 구문을 5초로 제한


세션이 아니라 쿼리에서 직접 설정할 수도 있는데, 이땐 아래와 같이 /*+ 로 시작하는 힌트 구문을 적용하면 된다.

    SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM employee -- 실행시간을 1초로 제한


참고로, 시스템 변수를 0으로 설정하면 무제한이지만, 힌트에서 0으로 설정할 경우엔 무제한이 아니라 시스템 변수를 따른다고 한다.

    SELECT /*+ MAX_EXECUTION_TIME(0) */ * FROM employee -- 시스템 변수를 따름



논의:

꽤 오래 전부터 종종 디비 서버의 CPU가 100%가 되면서 힘들어하곤 했다.
여러 원인이 있었고, 해결하기 위해 여러 방법(쿼리 수정, 실행 시간 제한, 리드 리플리케이션 등등)을 적용했고, 지금은 안정화된 것 같아 보인다.
글쎄- 원인도 복합적이고 해결책도 그랬겠지만, 실행 시간을 제한하기로 한 건 좋은 선택이었던 것 같다.



참고:


반응형
댓글
공지사항