[자바 성능 튜닝 이야기] 반드시 튜닝해야 하는 대상은?
모든 화면 다 분석할 수 있으면 좋다. 그러긴 쉬지 않으니 APM 같은 툴 활용도 좋다.
모든 화면 다 분석하는 건 비용적으로 크다. 상위 20퍼센트의 부유층이 전체 부의 80퍼센트를 소유하듯
사용량의 20퍼센트를 차지하는 화면을 튜닝하자.
웹로그를 분석하는게 좋을 수 있다.
이런 경우 있엇다.
기존 서버 구성은 두 대의 WAS서버, 각 서버에는 인스턴스 3개가 서로 클러스터링. 화면 하나에 한 시간 당 10만 번 이상의 호출이 있었다.
그래서 서버에 문제가 발생하였다.
문제를 진단했다.
- 호출되는 화면의 응답속도는 0.2 ~ 0.3 초로 양호하다.
- 검색 시 DB 쿼리 수행 시간 대 애플리케이션 수행 시간의 비율은 8:2로, DB에서 많은 응답 시간이 소요되었다.
- 검색 조건 화면을 로딩할 때 DB에서 3회 검색을 수행한다.
화면에서 실행되는 쿼리는 화면 1회 수행마다 총 49번 이었다. 데이터르 가져오기 위한 쿼리도 있었지만, 코드성 데이터를 가져오기 위한 쿼리도 존재했다. static이나 캐시로 처리한다
또 하나의 문제는 검색 조건 화면을 로딩 할때 DB쿼리를 수행한다. 시스템의 표준 때문에 이러한 현상이 발생한다.
이 부분이 필요없어서 제거하였다.
얼마나 많이 요청하는지 알아보자..
- 포틀릿이란 웹 유저들이 사용할 수 있는 사용자 인터페이스 컴포넌트다. 유저가 마음대로 우치 수정, 추가, 삭제할 수 있다.
성능 튜닝을 위한 기초 법칙
암달의 법칙
성능 개선율 = 1 / (1-p) + p/s
p는 개선 가능한 부분, s는 향상 정도
개선 가능 부분 이 100%이고 2배 성능향상이 있었다면
성능 개선율 = 1 / (1/2) = 2
성능 개선율은 2. 2배가 된다.
성능 튜닝 step by step
1) 원인 파악
2) 목표 설정
3) 튜닝 실시
4) 개선율 확인
5) 결과 정리 및 반영
- 원인 파악: 어디가 병목인지 확실히 파악해야한다. 오랜시간이 걸ㄹㄹ 수 있다.
- 목표 설정: 목표 설정을 하자
- 튜닝 실시: 코드를 최적화하며 튜닝하는 단계. 가자 좋은 방법은 프로파일링 툴이나 APM 같은 툴을 사용하는 것이고, 개선이 얼마나 도었는지를 확인하려면 JMH나 캘리퍼와 같은 성능 비교 도구를 사용하는 것도 큰 도움이 될 수 있다.
- 개선율 확인: 개선율을 확인한 다음 결과가 만족스럽지 않다면 다시 튜닝을 해야한다.
- 정리 및 반영: 아주 간단하게라도 어딘가에 정리하는 것이 좋다. 가장 좋은건 파워 포인트나워드 같은 문서지만, 시간이 좀 걸린다.회사 위키도 좋다.
성능 튜닝의 비법
- 하나만 보지 말아라.
- 큰 놈을 없애라.
- 깊게 알아야 한다.
- 결과 공유는 선택이 아닌 필수!
하나만 보지 말아라
성능상 이슈가 있어도 절대 하나만 보면 안된다.
대상 | 세부 대상 | |||||
---|---|---|---|---|---|---|
서버 장비 | CPU, Network, DIsk, Memory 등 | |||||
자바 애플리케이션 | 스레드 풀 설정, DB Connection Pool 설정, Cache 설정, GC 설정, Heap 크기 설정, 검증되지 않은 프레임워크의 버그, 개발된 애플리케이션 등 | |||||
웹서버 | 프로세스 개수 설정, Connector 설정, 개발된 모듈의 버그 등 | |||||
서버 OS | OS 커널, OS 설정, 수행 중인 프로세스 등 | |||||
네트워크 | 사용자의 네트워크의 종류, 사용자의위치, L4 및 Switch 등 네트워크 장비, 방화벽 등 | |||||
클라이언트 | 클라이언트 장비의 CPU, Network, Disk, Memory, OS 등의 성능, 클라이언트애플리케이션 등.. | |||||
큰 놈을 없애라
잔챙이 백날 튜닝해봤자 효과 없다. 애플리케이션 요청의 상위 80퍼센트에 해당하는 요청을 처리하자. 성능 튜닝을 하다보면 중심이 되는 라이브러리에 문제가 발생하는 경우도 많다. 이 때 선택은 둘 라이브러리 튜닝이나 교체. 이러 경우 발생하는 것을 방지하는 가장 조은 방법은 시스템의 코어부분이 개발 완료 되었을떄 성능 테스트를 통해서 사용중인 라이브러리의 성능을 검증하는 것이다.
서버의 성능도 큰 영향을 미치지만 클라이언트의 성능도 무시 못한다. 애플리케이션의 로딩이 어떻게 되는지 확인해보자.
깊게 알아야한다.
개발 언어, OS, DB, N/W, 서버 등 대충 알면 튜닝하기 힘들다. 하나라도 전문가가 되어야한다.
결과 공유는 선택이 아닌필수
잘못하면 오랜 기간 동안 힘ㄷ르게 작업한 것이 인정받지 못할 수도 있다. 결과 정리를 할 때 꼭 포함되어야 하는 내용은 다음과 같다.
개요: 튜닝을 실시한 배경
튜닝 환경: 튜닝을 실시하고 성능을 측정한 서버 및 툴에 대한 상세한 내용
튜닝 결과: 튜닝 전과 후의 결과를 비교
결론: 어느 부분을 어떻게 변경하는 것이 가장 큰 효과를 줄지, 튜닝작업을 진행한 담당자의 의견 등을 포함.
확실한 결과 위주로 포함하자. 정확한 수치로
개선 효과가 가장 큰 것부터 나열하자
개발한 사람의 심기를 나쁘게 하지 말자
그래프 그릴 때 유의 사항
신중하게 생각하면서 그리자. 의미없는거 30 개 만드는거보다 그래프 하나로 모든 상황을 볼 수 있도록 그리는 것만큼 예술적인게 없다.
전반적으로,
- 그래프의 타이틀은 반드시 추가하자. 그렇지 않으면 비슷한 다른 그래프와 혼동될 수 있다.
- 그래프의 라벨은 가능하면 X축 하단에 넣자. 그래야 그래프의 내용을 보다크게 보여줄 수 있다.
- 산포도를 보여줄 때 , 데이터를나타내는 각 점은 되도록이면 작게 표현하자. 그래야 데이터가 어떻게 분포되었는지 쉽게 볼 수 있다.
Y축을 그릴 때,
- 정수 값일 경우 1,000 단위에 콤마를 추가해주자.
- 소수까지 내려가는 값일 경우 적어도 3 자리를 보여주자. 하지만 이 경우는 그때 그때 다르다.
- 주 단위를 표시하는 경우에는 그래프 내에 주 단위가 4개 이상 표시되지 않는 것이 좋다. 그렇지 않으면, 그래프데이터가 잘 보이지 않는다.
x축을 그릴 때,
- X축의 주 단위는 10개 내외로 보여 주는 것이 좋다.
- 주 단위를 선으로 보여줄 지 , 눈금으로만 보여줄 지는 상황에 맞게 결정하자.
애플리케이션에서 점검해야 할 대상들
패턴과 아키텍처는 잘 구성되었느가?
너무 많은 패턴을 사용하지 않았는가? 유지보수성 떨어지고, 추적하기 어려워. 좋은 패턴이라고 무작정 적요하기 보다 꼭 필요한 패턴만 사용
데이터를 리턴할 때 TO 패턴을 사용하였는가? 아니면 Collection 관련 클래스를 사용하였는가? 데이터 주고 받는 시간을 절약하기 위해서 일반적으로 TO 패턴을 사용한다. 때에 따라 Collection 관련 클래스를 사용하기도 한다. 이러한 패턴을 적용하지 않거나 관련 표준을 정하지 않고 개발을 하면 시스템의 응답 시간도 영향이 있겟지만 유지보수성이 떨이고 HashMa으로 데이터를 주고 받으면 소스를 완전히 뜯어보지 않는이상 개발자만 어떤 키와 값이 들어있는지 알게된다.
서비스 로케이터 패턴은 적용이 되었 있는가? 애플리케이션에서 필요한 대상 찾는 룩업 작업을 할 때 소요되는 시간을 줄일 수 있다. 서버의 CPU 사용량이 높지 않을 때 응답속도가 느리다면, 서비스 로케이터를 적용해야 하는 부분이 있지 않은가 고려
기본적인 애플리케이션 코딩은 잘 되어 있는가?
- 명명 규칙은 잘 지켰는가?
- 필요한 부분에 예외 처리는 되어 있는가? 사용자가 아무런 응답을 못 받고 서비스를 떠날 수 있다.
- 예외 화면은 지정되어 있는가? 따로 안 만들어 놓으면 우리가 사용하는 서버의 종류가 어떤 것인지 알게된다. 때에 따라 시스템에 어떤 클래스가 있는지도 확인이 가능하다
- 예외 정보를 e.printStackTrace()로만 처리하고 있지 않은가? 이를 호출하면 서버에 스택 정보를 취합해야해서 많은 부하를 줄 수 있다. 예외로그 간겨랗게 하자.
- System.gc() 메서드가 소스에포함되어 있지 않은가? 언제 GC가 되어야하는지 신경 쓰지말자.
- System.exit() 메서드가 소스에 포함되어 있지 않은가? 이 메서드가수행되면 WS의 프로세스가 죽는다. 스레드가 죽는게 아니라 프로세스가 죽으니 반드시 제거하자.
- 문자열을 계속 더하도록 코딩하지는 않았는가? String을 한 두개 더하는건 문제가 안되겠지만 다수의 스트링을 더하는 것은 GC의 대상이 되어 시간이 소요딘다. JDK 5.0부터는 자동으로 StringBUilder클래스로 변환을 해주지만 루프를 수행하면서더할 경우 컴파일러도어쩔 수 없으니 필요에 따라 StringBuffer, StringBuilder를 쓰자.
- StringBuffer나 StringBuilder도 제대로 사용하였는가? append API로 확장하자.
- 무한 루프가 작동할 만한 코드는 없는가? for 루프는 반복 회수를 지정하고 사용해 큰 문제 적지만 while(true)는 좀 위험하다.
- static을 나발하지 않았는가? 심각한 오류를 발생시킬 수 있다. 직급 높은 사람한테 static 써도 괜찮을 지 물어보자.
- 필요한 부분에 synchronized 블록을 사용하였는가? 필요없는 부분에 쓰면 성능에 안 좋다.
- IO가 계속 발생하도록 개발되어 있지 않은가? 설정 파일을 매번 파일에서읽도록 개발하는 것 하지마.
- 필요 없는 로그는 다 제거했는가? 로그레벨이 DEBUG가 아니니 괜찮겠지란 생각은 버려라.
- 디버그용 System.out.println은다 제거하였는가? 로그 레벨을 바꾼다고 해도 지워지지 않는게 이것이다. 지우자
웹 관련 코딩은 잘 되어 있는가?
- JSP의 include는 동적으로했는가? 아니면 정적으로 했는가? 동적으로하면 서버에도 부하를 줄 뿐만ㅇ ㅏ니라 응답시간에도 영향을 준다.
- 자바 빈즈는 너무 많이 사용하지 않았나? 수십개씩 쓰진 않았나? TO 하나에 처리할 수 있다면 그렇게 하자
- 태그 라이브러리는 적절하게 사용했나? 많은 양의 데이터를 태그 라이브러리로처리할 경우 성능 영향 크다.
- EJB는 적절하게 사용하였나? EJB를 많이 사용한다고 해서 시스템이 안정화 되는 것은 아니다. EJB 하나 하나는 일반 클래스보다 맣은 메모리를 점유해야한다.
- 이미지 서버를 사용할 수 있는 환경인가? 정적인 컨텐츠가 많고 사용자의 요청이 많은 경우 웹 서버 이외에 이미지 서버를 사용할 수 있다. 웹 서버에서 파일을 읽어처리하는 것보다효율적이다.
- 사용 중인 프레임워크는 검증되었느가? 그렇지 않다면 성능 테스트 및 메모리 프로파일리을 통해서 해당 프레임워크의 안정성을 확인해 놓아야 한다.
DB 과련 코딩은 잘 되어 있는가?
- 적절한 JDBC 드라이버를 사용하는가? 현재 DB에 맞는 버전의 JDBC를 사용하고 있는가? 간혹 버그가 있는 JDBC를 사용해 성능이 나오지 앟는 경우 발생. 가능한 가장 최신의 WAS 및 DB 벤더에서 추천하는 문제 없는 JDBC를 사용하자.
- DB Connection, Statement, ResultSet은 잘 닫았는가? Finally 구문을 사용해 명시적으로 닫던가 그렇지 않으면 연결이 부족해져 사용 불가능해 진다.
- DB Connection Pool은 잘 사용하고 있는가? 규모가 크면 클수록 표준을 따르지 않을 확률도 커진다. DB 연결 부분을 개발자 역량에 맡기면 여러 형태로 DB에 연결하게 될 것이다. 관련 표준을 반드시 정하고 DB Connetion Pool은 반드시 사용해야 DB의 리소스를 보다효율적으로 사용할 수 있다.
- 자동 커밋 모드에 대한 고려는 하였는가? 기본 커밋 모드는 자동 커밋으로 되어 있다. 하지만 조회성 프로그램도 자동 커밋 여부를 지저하게 되면, 약간의 응답 시간 저하가 발생하게 된다. 반드시 필요하지 않은 경우에는 자동 커밋을 하도록 하자.
- ResultSet.last() 메서드를 사용하였는가? 전체 건수를 처리하기 위해서last() 메서드를 사용했는지 확인해야한다. 데이터의 건수가 많을 수록 응답 시간이 느려진다. 쿼리를 두 번 수행하더라도 전체 건수를 가져오기위한 last() 메서드는 자제하자.
- PreparedStatements를 사용하였는가? Statement를 사용해 매번 쿼리를 수행할 때마다 QL 쿼리를 컴파일 하게 된다. 이 작업은 DB에 많은 부하를 준다. 그러므로 쿼리 문장이 계속 동적으로 변경되어야 하는 경우를 제외하면 대부분의 경우에는 PreparedStatements를 사용하자.
서버의 설정은 잘 되어 있는가?
- 자바 VM 관련 옵션들은 제대로 설정되어 있는가? 64비트 기반의 시스템을 사용하면서 -d64 옵션을 사용하지 안는다면, 그냥 32비트 시스템이 운영된다. 또한 클래스패스는 순차적으로 인식된다. 여러 JAR 파일 중 만약 가은 패키지 명에 같은 클래슥 존재할 경우 앞에 명시한 클래스패스에 우선권이 있다.
- 메모리는 몇 MB로 설정해 놓았는가? 가끔 서버의 메모리 설정을 하지 않고, 성능이 나오지 않는다하는 사이트가 있다. 설정하지 않으면 서버는 기본 64MB로 시작한다.
- GC 설정은 어떻게 되어 있는가? 성능 테스트 통해서 우리 시스템에 맞는 GC 옵션을 설정하자
- 서버가 운영 모드인지 개발 모드인지 확인하였는가? 서버가 개발 모드라면 , WAS는 주기적으로 변경된 클래스가 있는지 확인할 것이다. 이 작업은 서버에 많은 부하를 주게된다. 서버가 주기적으로 느려진다면 이 부분은 반드시 확인하자.
- WAS의 인스턴스가 몇 개 기동되고 있는가? WAS에서 하나의 인스턴스 당 적어도 한 개는 있어야 제대로 운영이가능하다. CPU가 4개 있는데 인스턴스가 30개 정도 있는 것은 아닌지 확인하자
- JSP Precompile 옵션은 지정해 놓았는가? 서버를 기동할 때 JSP를 미리 컴파일하도록 해놓으면 사용자는 JSP가 수정이 되었는지 여부와상관없이일정한 응답속도를 받을 것이다. 서버 기동 시 프리컴파일함으로 시간이 좀 걸린다.
- DB Connection Pool 개수와 스레드 개수는 적절한가? 스레드 개수가 DB Connection Pool의 수 보다작으면 안되다. 보통 스레드를 10개 더 많게 해야한다. 만약 DB Connection Pool의 설정을 기본으로 두고하면 10명 이상 동시사용을 처리하지 못할 것이다.대부분 WAS의 Connection Pool의 초기 값은 10~20개 이다.
- 세션 타임아웃 시간은적절한가? 세션을 더이상 사용하지 않는다면 삭제해야한다.
- 검색 서버가 있따면 , 검색 서버에 대한설정 및 성능 테스트를 하였는가?
모니터링은 어떻게 하고 있는가?
- 웹 로그(Access log)는 남기고 있는가? 애플리케이션 사용량과 자주 사용하는 애플리케이션 분석 위해 남겨두자
- verbosegc 옵션은 남기고 있는가? GC가 어떤 형태로 발생하는지를 확인하고자 한다면, 반드시 verbosegc 옵션을 사용하자. 메모리 크기나 GC 옵션을 변경하기 위해서라도 verbosegc 옵션은 유용할 것이다.
- 각종 로그 파일에 대한 규칙은 있는가? 로그파일을 그냥 몇 기가씩 쌓고 있진 않나? 일별로 로그를 쌓도록 옵션을 지정하면 특정 이슈가 발생한 날짜의 데이터를 분석하기에도좋고 백업을 하기에도 편리하다.
- 서버의 시스템 사용률은 로그로 남기고 있는가? WAS나 DB가 얼마나 사용을 하고 있는지 로그를 남겨야 한다. 대부분의 사이트에는 SMS나 APM을 설치하여 서버를 모니터링 하고 있겠지만 그렇지 않은 사이트에서는 서버의사용량을 유닉스의 vmstat이나 sar 명령어를 사용해서 남겨야 시간대별 서버 사용량을 구할 수 있다. 서버 증설 시에도 유용
- 모니터링 툴은 사용 중인가? ㅁWAS 모니터링을 위해 툴은 쓰나? 시스템 로그만 사용해 시스템의 상태를 저거하는 시대는 사라졌다. 서버가 많은 부하를 받고 있지 않다면 JMX 기술을 사용하여 서버를 모니터링 하는 것도 좋은 방법이고 JDK 5.0 이상에서 제공하는 JConsole을 사용하는 것도 좋다.
- 모니터링 툴에 대한 설정은 적절하게 되어 있는가? 제대로 활용 못하면 소용없다. 모든 메서드에 프로파일링 지정하면 성능에 영향 준다 꼭 필요한 부분에만 설정하자.
- 서버가 갑자기 코어 덤프를 발생시키지 않는가? 서버에서 코어 덤프를 발생시키는 경우의 수는 굉장히 많다. 만약 서버가지소적으로 코어덤프를 발생시킨다면 다음을 점검해보자.
1) 10,000 건 이상 조회하는 것이 있나 확인해야 한다. 한꺼번에 많은 양읟 ㅔ이터를 처리하려면 많은 메모리가 필요한데, 이 때 코어덤프가 발생할 수 있다. 데이터를 처리하는데 필요한 메모리가 지정된 메모리 크기보다클 수도 있기 때문이다.
2) 메모리 릭이 있는지 확인해야한다. 메모리를 점유하고 해제하지 않는 로직이 있으면 서버를 매일 재기동해도 메모리는 점차적으로 부족해진다. 서버가 기동딘 후 힙 덤프를 받아 놓은뒤 , 운영 중, 후 에 각각 덤프를 받아서 메모리 사용량의 추이를 확인하자.
- 응답 시간이 너무느리지 않은가? 응답 시간이 느려지는 이유는 상상을 초월할 정도로 많다. 그러므로 이 문제를 해결하기 위해서는 상황을 정확히 판단해야한다. WAS단 이후에서 느린것인지, 그 이전 단계인 네트워크나 웹 서버에서 느린지확인해야 한다. 만약 WAS에서 느리다면, 정확히 어느 부분에서 느린지 프로파일링을 해서 확인해야한다.