디자인 패턴 꼭 써야 한다.
J2EE 디자인 패턴이란?
디자인 패턴
- 시스템을 만들기 위해서 전체 중 일부 의미 있는 클래스들을 묶은 각각의 집합
각 패턴의 특징
- Intercepting Filter 패턴
- 요청 타입에 따라 다른 처리를 하기 위한 패턴이다.
- Front Controller 패턴
- 요청 전후에 처리하기 위한 컨트롤러를 지정하는 패턴이다.
- View Helper 패턴
- 프레젠테이션 로직과 상관 없는 비즈니스 로직을 헬퍼로 지정하는 패턴이다.
- Composite View 패턴
- 최소 단위의 하위 컴포넌트를 분리하여 화면을 구성하는 패턴이다.
- Service to Worker 패턴
- Front Controller와 View Helper 사이에 디스패처를 두어 조합하는 패턴이다.
- Dispatcher View 패턴
- Front Controller와 View Helper로 디스패처 컴포넌트를 형성한다. 뷰 처리가 종료될 때까지 다른 활동을 지연한다는 점이 Service to Worker 패턴과 다르다.
- Business Delegate 패턴
- 비즈니스 서비스 접근을 캡슐화하는 패턴이다.
- Service Locator 패턴
- 서비스와 컴포넌트 검색을 쉽게 하는 패턴이다.
- J2EE 패턴 중 성능과 가장 밀접한 패턴
- Session Facade 패턴
- 비즈니스 티어 컴포넌트를 캡슐화하고, 원격 클라이언트에서 접근할 수 있는 서비스를 제공하는 패턴이다.
- Composite Entity 패턴
- 로컬 엔티티 빈과 POJO를 이용하여 큰 단위의 엔티티 객체를 구현한다.
- Transfer Object 패턴
- 일명 Value Object 패턴이라고 많이 알려져 있다. 데이터를 전송하기 위한 객체에 대한 패턴이다.
- 성능에 직접적으로 많은 영향을 미치지는 않지만, 애플리케이션 개발 시 반드시 사용해야 하는 패턴
- Tranfer Object Assembler 패턴
- 하나의 Transfer Object로 모든 타입 데이터를 처리할 수 없으므로, 여러 Transfer Object를 조합하거나 변형한 객체를 생성하여 사용하는 패턴이다.
- Value List Handler 패턴
- 데이터 조회를 처리하고, 결과를 임시 저장하며, 결과 집합을 검색하여 필요한 항목을 선택하는 역할을 수행한다.
- Data Access Object 패턴
- 일명 DAO라고 많이 알려져 있다. DB에 접근을 전달하는 클래스를 추상화하고 캡슐화한다.
- Service Activator 패턴
- 비동기적 호출을 처리하기 위한 패턴이다.
성능 개선은 물론 개발과 유지보수의 편의를 위해서 앞서 명시된 여러가지 J2EE 패턴 중에 적어도 Business Delegate, Session Facade, Data Access Object, Service Locator, Transfer Object 패턴은 적용하자.
내가 만든 프로그램의 속도를 알고 싶다
시스템의 성능이 느릴 때 가장 먼저 해야 하는 작업은 병목 지점을 파악하는 것이다.
애플리케이션 속도에 문제가 있을 떄 분석하기 위한 툴
- 프로파일링 툴
- 대부분 느신 메서드, 느린 클래스를 찾는 것을 목적으로 한다.
- APM 툴
- 목적에 따라 용도가 상이하다. (문제점 진단, 시스템 모니터링 및 운영)
프로파일링 툴이란?
프로파일링 툴이 기본적으로 제공하는 기능
- 응답 시간 프로파일링 기능
- 하나의 클래스 내에서 사용되는 메서드 단위의 응답 시간을 측정한다.
- 응답 시간 프로파일링을 할 때는 보통 CPU 시간과 대기 시간, 이렇게 두 가지 시간이 제공된다.
- 메모리 프로파일링
- 잠깐 사용하고 GC의 대상이 되는 부분을 찾거나, 메모리 부족 현상이 발생하는 부분을 찾는다.
- 클래스 및 메서드 단위의 메모리 사용량이 분석된다.
System 클래스
가장 간단하게 측정할 수 있는 방법은 System 클래스에서 제공하는 메서드를 활용하는 것이다.
- System.nanoTime(), System.currentTimeMillis() 메서드 사용
- JMH 사용
- 일일이 케이스를 구성할 필요 없이 각 케이스별로 테스트를 수행하고 그 결과를 확인할 수 있다.
- 어떤 API의 메서드를 사용할지, 성능 차이가 도대체 얼마나 발생하는지 확인하기에 매우 좋은 툴이다.
JVM에서 사용할 수 있는 설정
- 속성(Property)값
- JVM에서 지정된 값들
- 환경(Environment)값
- 장비(서버)에 지정되어 있는 값들
운영중인 코드에 절대로 사용해서는 안되는 메서드
- static void gc()
- 자바에서 사용하는 메모리를 명시적으로 해제하도록 GC를 수행하는 메서드다.
- static void exit(int status)
- 현재 수행중인 자바 VM을 멈춘다.
- static void runFinalization()
- 참조 해제 작업을 기다리는 모든 객체의 finalize() 메서드를 수동으로 수행해야 한다.
정리
- 프로젝트의 상황에 맞는 APM 및 프로파일링 툴을 잘 선택해야 한다.
- 나노초, 밀리초차이가 쌓이면 1초, 10초, 100초가 된다는 점을 잊지 말자.
왜 자꾸 String을 쓰지 말라는 거야
GC를 하면 할수록 시스템의 CPU를 사용하게 되고 시간도 많이 소요된다. 그래서 메모리 사용을 최소화하는 것은 당연한 일이다.
상황에 맞는 사용
- String
- 짧은 문자열을 더할 경우 사용한다.
- StringBuffer
- 스레드에 안전한 프로그램이 필요할 때나, 개발 중인 시스템의 부분이 스레드에 안전한지 모를 경우 사용하면 좋다.
- 만약 클래스에 static으로 선언한 문자열을 변경하거나, singleton으로 선언된 클래스에 선언된 문자열일 경우에는 이 클래스를 사용해야만 한다.
- StringBuilder
- 스레드에 안전한지의 여부와 전혀 관계 없는 프로그램을 개발할 때 사용하면 좋다.
- 만약 메서드 내에 변수를 선언했다면, 해당 변수는 그 메서드 내에서만 살아있으므로, StringBuilder를 사용하면 된다.
만약 WAS나 시스템이 JDK 5.0 이상을 사용한다면, 컴파일러에서 자동으로 StringBuilder로 변환하여 준다. 하지만 반복 루프를 사용해서 문자열을 더할 때는 객체를 계속 추가한다는 사실에는 변함이 없다.
어디에 담아야 하는지...
java.util.concurrent 패키지에는 문제를 해결해 줄 수 있는 각종 클래스들이 존재한다.
일반적인 웹을 개발할 때는 Collection 성능 차이를 비교하는 것은 큰 의미가 없다.
각 클래스에는 사용 목적이 있기 때문에 목적에 부합하는 클래스를 선택해서 사용하는 것이 바람직하다.
사용하는 목적에는 맞는데 해당 메서드의 성능이 잘 나올지 확실치 않은 경우에는 JMH를 사용하여 직접 성능 측정을 해보자.
지금까지 사용하던 for 루프를 더 빠르게 할 수 있다고?
switch-case
- switch-case 문은 작은 숫자부터 큰 숫자를 비교하는 게 가장 빠르다.
- case의 수가 많으면 많을수록 switch-case에서 소요되는 시간이 오래 걸린다.
for
- 메서드 반복 호출을 없애자. (size())
- 반복 구문에서의 필요 없는 반복을 없애자.
작은 부분이 차지하는 반복 구문이 큰 성능 저하를 가져올 수도 있다는 것을 명심하자.
static 제대로 한번 써 보자.
static 잘 활용하기
- 자주 사용하고 절대 변하지 않는 변수는 final static으로 선언하자
- 설정 파일 정보도 static으로 관리하자
- 코드성 데이터는 DB에서 한 번만 읽자.
static 잘못 쓰면 이렇게 된다.
- 응시자의 합격 여부를 잠깐 담아 놓기 위해
private static boolean successFlag;
로 지정해 놓는 경우- 수십 명이 동시에 해당 서블릿을 호출할 경우, successFlag를 true로 처리해 놓은 상황에서 다른 사용자의 요청이 처리되어 false로 바뀐다면, 그 사람은 완전히 다른 결과를 받게 된다.
static과 메모리 릭
static으로 선언한 부분은 GC가 되지 않는다. ArrayList 객체를 static으로 선언하고 계속해서 데이터가 쌓일 경우 OutOfMemoryError가 발생하게 된다.
메모리릭의 원인은 메모리의 현재 상태를 (메모리의 단면을) 파일로 남기는 HeapDump라는 파일을 통해서 확인 가능하다.
- JDK/bin 디렉터리에 있는 jmap이라는 파일을 사용하여 덤프를 남길 수 있다.
만약 static을 사용하는 것이 걱정된다면, 아예 쓰지를 말자. 모르고 시스템이 잘못되는 것보다 아예 안 쓰는 것이 더 안전하다.
클래스 정보, 어떻게 알아낼 수 있나?
reflection 관련 클래스들
Class 클래스
- 클래스에 대한 정보를 얻을 때 사용하기 좋고, 생성자는 따로 없다.
- ClassLoader 클래스의 defineClass() 메서드를 이용해서 클래스 객체를 만들 수도 있지만, 좋은 방법은 아니다. 그보다는 Object 클래스에 있는 getClass() 메서드를 이용하는 것이 일반적이다.
- 현재 클래스의 이름을 알고 싶으면 다음과 같이 사용하면 된다.
String currentClassName = this.getClass().getName();
- 그런데, 여기서 getName() 메서드는 패키지 정보까지 리턴해 준다. 클래스 이름만 필요한 경우에는 getSimpleName() 메서드를 사용하면 된다.
Method 클래스
- 메서드에 대한 정보를 얻을 수 있다. 하지만, Method 클래스에는 생성자가 없으므로 Method 클래스의 정보를 얻기 위해서는 Class 클래스의 getMethods() 메서드를 사용하거나 getDeclaredMethod() 메서드를 써야 한다.
Field 클래스
- 클래스에 있는 변수들의 정보를 제공하기 위해서 사용한다.
- Method 클래스와 마찬가지로 마찬가지로 생성자가 존재하지 않으므로 Class 클래스의 getField() 메서드나 getDeclaredFields() 멤서드를 써야 한다.
나머지 reflection 관련 클래스는 앞에서 설명한 세 가지 클래스와 비슷하게 사용할 수 있다.
reflection 관련 클래스를 사용한 예
클래스 정보를 가져오는 부분과 'JMX'를 연계시킨다면, 서버에서 사용하는 클래스의 정보를 가져오는 막강한 모니터링 기술을 제공할 수도 있을 것이다.
reflection 클래스를 잘못 사용한 사례
일반적으로 로그를 프린트할 때 클래스 이름을 알아내기 위해서 사용하는 방법
this.getClass().getName()
- getClass() 메서드를 호출할 때 Class 객체를 만들고, 그 객체의 이름을 가져오는 메서드를 수행하는 시간과 메모리를 사용한다.
- instanceOf를 사용하는 것이 클래스의 이름으로 해당 객체의 타입을 비교하는 방법보다 낫다.
정리
reflection 관련 클래스를 사용하면 클래스의 정보 및 여러 가지 세부 정보를 알 수 있어 매우 편리하다. 하지만 로그에서 사용하기 위해서라면, 클래스 객체를 얻기보다는 클래스의 이름을 복사해서 붙여넣는 것이 나중에 소스를 확인할 때에도 더 깔끔할 것이다.
메타 데이터 정보는 JVM의 Perm 영역에 저장된다. 만약 Class 클래스를 사용하여 엄청나게 많은 클래스를 동적으로 생성하는 일이 벌어지면 Perm 영역이 더 이상 사용할 수 없게 되어 OutOfMemoryError가 발생할 수도 있으니, 조심해서 사용하자.
synchronized는 제대로 알고 써야 한다
언제 동기화를 사용해야 할까?
- 하나의 객체를 여러 스레드에서 동시에 사용할 경우
- static으로 선언한 객체를 여러 스레드에서 동시에 사용할 경우
반드시 필요한 부분에만 동기화를 사용해야 성능 저하를 줄일 수 있다.
static 사용 시 동기화 할 때는 클래스 변수를 사용하는 메서드를 동기화하기 위해서는 해당 메서드가 클래스 메서드이어야 한다.
IO에서 발생하는 병목 현상
로그는 반드시 필요한 내용만 찍자
JSP와 서블릿, Spring에서 발생할 수 있는 여러 문제점
JSP의 라이프 사이클
- JSP URL 호출
- 페이지 번역
- JSP 페이지 컴파일
- 클래스 로드
- 인스턴스 생성
- jspInit 메서드 호출
- _jspService 메서드 호출
- jspDestory 메서드 호출
여기서 해당 JSP 페이지가 이미 컴파일되어 있고, 클래스가 로드되어 있고, JSP 파일이 변경되지 않았다면, 가장 많은 시간이 소요되는 2~4 프로세스는 생략된다.
서블릿의 라이프 사이클
- WAS의 JVM이 시작한 후에는,
- Servlet 객체가 자동으로 생성되고 초기화 되거나,
- 사용자가 해당 Servlet을 처음으로 호출했을 때 생성되고 초기화 된다.
- 그 다음에는 계속 '사용 가능' 상태로 대기한다.
- 그리고 중간에 예외가 발생하면 '사용 불가능' 상태로 빠졌다가 다시 '사용 가능' 상태로 변환되기도 한다.
- 그리고 나서, 해당 서블릿이 더 이상 필요 없을 때는 '파기' 상태로 넘어간 후 JVM에서 '제거'된다.
- '사용 가능' 상태가 된 이상 대부분의 서블릿은 JVM에 살아 있고, 여러 스레드에서 해당 서블릿의 service() 메서드를 호출하여 공유한다.
여러 스레드로부터 뭇매를 맞으면, 데이터가 꼬여서 원하지 않는 값들이 출력될 수도 있다. 그러므로, service() 메서드를 구현할 때는 멤버 변수나 static한 클래스 변수를 선언하여 지속적으로 변경하는 작업은 피하자.
스프링 프레임워크 간단 정리
가장 큰 특징은 복잡한 애플리케이션도 POJO로 개발할 수 있기 때문에, 개발자가 보다 쉽게 자신이 작성한 코드를 테스트 할 수 있다. 그래서 더 빠르고 쉽게 문제를 확인할 수 있으며 이는 곧 높은 개발 생산성으로 이어진다.
스프링의 핵심 기술
- Dependency Injection
- 어떤 객체가 필요로 하는 객체를 자기 자신이 직접 생성하여 사용하는 것이 아니라 외부에 있는 다른 무언가로부터 필요로 하는 객체를 주입 받는 기술이다.
- 스프링은 다양한 의존성 주입 방법을 제공하고 있다.
- Aspect Oriented Programming
- 트랜잭션, 로깅, 보완 체크 코드 같은 코드들을 실제 비즈니스 로직과 분리할 수 있도록 도와주는 것이 바로 AOP다.
- 스프링은 AspectJ와 손쉽게 연동하는 방법을 제공할 뿐 아니라, AspectJ 보다 훨씬 더 사용하기 간편한 방법을 사용한 스프링 AOP를 제공해준다.
- 이 기술을 잘 활용하면 핵심 비즈니스 코드의 가독성을 높여준다.
- Portable Service Abstraction
- 스프링은 비슷한 기술을 모두 아우를 수 있는 추상화 계층을 제공하여, 사용하는 기술이 바뀌더라도 비즈니스 로직의 변화가 없도록 도와준다.
- 제대로만 개발했다면, 이러한 일이 발생했을 때 스프링의 의존성 주입 기능으로 사용할 객체만 바꿔주면 된다.
스프링 프레임워크를 사용하면서 발생할 수 있는 문제점들
스프링 프레임워크를 사용할 때 성능 문제가 가장 많이 발생하는 부분은 '프록시'와 관련되어 있다.
스프링 프록시는 기본적으로 실행 시에 생성된다. 따라서, 개발할 때 적은 요청을 할 때는 이상이 없다가, 요청량이 많은 운영 상황으로 넘어가면 문제가 나타날 수 있다.
스프링이 프록시를 사용하게 하는 주요 기능은 바로 트랜잭션이다.
- @Transactional 어노테이션을 사용하면 해당 어노테이션을 사용한 클래스의 인스턴스를 처음 만들 때 프록시 객체를 만든다.
이밖에도, 개발자가 직접 스프링 AOP를 사용해서 별도의 기능을 추가하는 경우에도 프록시를 사용하는데, 이 부분에서 문제가 많이 발생한다.
- 개발자가 직접 작성한 AOP 코드는 예상하지 못한 성능 문제를 보일 가능성이 매우 높다.
- 따라서, 간단한 부하 툴을 사용해서라도 성능적인 면을 테스트해야만 한다.
추가로, 스프링이 내부 메커니즘에서 사용하는 캐시도 조심해서 써야 한다.
예를 들어 스프링 MVC에서 작성하는 메서드의 리턴 타입으로 다음과 같은 문자열을 사용할 수 있다.
@RequestMapping("/member/{id}") public String hello(@PathVariable int id) { return "redirect:/member/" + id; }
이렇게 문자열 자체를 리턴하면 스프링은 해당 문자열에 해당하는 실제 뷰 객체를 찾는 메커니즘을 사용하는데, 이 때 매번 동일한 문자열에 대한 뷰 객체를 새로 찾기 보다는 이미 찾아본 뷰 객체를 캐싱해두면 다음에도 동일한 문자열이 반환됐을 때 훨씬 빠르게 뷰 객체를 찾을 수 있다. 스프링에서 제공하는 ViewResolver 중에 자주 사용되는 InternalResourceViewResolver에는 그러한 캐싱 기능이 내장되어 있다.
만약 매번 다른 문자열이 생성될 가능성이 높고, 상당히 많은 수의 키 값으로 캐시 값이 생성될 여지가 있는 상황에서는 문자열을 반환하는 게 메모리에 치명적일 수 있다. 따라서 이런 상황에서는 뷰 이름을 문자열로 반환하기보다는 뷰 객체 자체를 반환하는 방법이 메모리 릭을 방지하는 데 도움이 된다.
@RequestMapping("/member/{id}") public View hello(@PathVariable int id) { return new RedirectView("/member/" + id); }
정리
- 화면의 성능이 좋지 않을 때는 화면을 담당하는 JSP나 서블릿이 원인인 경우가 가끔 발생한다.
- include 구문도 정적인 구문을 사용할지, 동적인 구문을 사용할지 잘 선택하여야 한다.
- 자바 진즈를 너무 많이 사용하는 것은 성능에 많은 영향을 줄 수 있다.
- 태그 라이브러리도 상황에 맞게 사용해야 한다.
- 성능에 대한 부분도 중요하지만, 가장 중요한 것은 화면을 구성하는 것이다.
- 특히 에러 페이지를 어떻게 구성하느냐가 제일 중요하다.
- 에러 페이지는 오류가 발생했을 때 사용자가 즉시 문제 요청을 할 수 있게 하고, 담당자가 문제를 쉽게 확인할 수 있도록 구성해야 한다.
- 스프링 프레임워크에서 제공하는 각 어노테이션이 어떤 의미인지 정확히 알고 사용해야만 한다.
DB를 사용하면서 발생 가능한 문제점들
자바 기반 애플리케이션의 응답 속도를 지연시키는 대부분의 요인은 DB 쿼리 수행 시간과 결과를 처리하는 시간이다.
DB Connection과 Connection Pool, DataSource
우리가 사용하는 JDBC 관련 API는 클래스가 아니라 인터페이스이다.
JDK의 API에 있는 java.sql 인터페이스를 각 DB 벤더에서 상황에 맞게 구현하도록 되어 있다. 같은 인터페이스라고 해도, 각 DB 벤더에 따라서 처리되는 속도나 내부 처리 방식은 상이하다.
일반적으로 DB에 연결하여 사용하는 방법은 다음과 같다.
- 드라이버를 로드한다.
- DB 서버의 IP와 ID, PW등을 DriverManager 클래스의 getConnection 메서드를 사용하여 Connection 객체로 만든다.
- Connection으로부터 PreparedStatement 객체를 받는다.
- executeQuery를 수행하여 그 결과로 ResultSet 객체를 받아서 데이터를 처리한다.
- 모든 데이터를 처리한 이후에는 finally 구문을 사용하여 ResultSet, Prepared Statement, Connection 객체들을 닫는다. 물론 각 객체를 close할 때 예외가 발생할 수 있으므로, 해당 메서드에서는 예외를 던지도록 처리해 놓아야 한다.
여기서 가장 느린 부분은 Connection 객체를 얻는 부분이다. 사용자가 갑자기 증가하면 Connection 객체를 얻기 위한 시간이 엄청나게 소요될 것이며, 많은 화면이 예외를 발생시킬 것이다.
이렇게 Connection 객체를 생성하는 부분에서 발생하는 대기 시간을 줄이고, 네트워크의 부담을 줄이기 위해서 사용하는 것이 DB Connetion Pool이다.
지금은 모든 WAS에서 Connection Pool을 제공하고, DataSource를 사용하여 JNDI로 호출해 쓸 수 있다.
DB를 사용할 때 닫아야 하는 것들
객체를 닫는 순서는 ResultSet, Statement, Connenction 순이다.
ResultSet 객체가 닫히는 경우
- close() 메서드를 호출하는 경우
- GC의 대상이 되어 GC되는 경우
- 관련된 Statement 객체의 close() 메서드가 호출되는 경우
Statement 객체는 Connection 객체를 close() 한다고 해서 자동으로 닫히지 않는다. 다음 두 가지 경우에만 닫히므로, 반드시 close() 메서드를 호출하여 닫자.
- close() 메서드를 호출하는 경우
- GC의 대상이 되어 GC 되는 경우
Connection 객체가 닫히는 경우
- close() 메서드를 호출하는 경우
- GC의 대상이 되어 GC되는 경우
- 치명적인 에러가 발생하는 경우
GC가 있는데 close() 메서드를 굳이 호출하는 이유는, 자동으로 호출되기 전에 관련된 DB와 JDBC 리소스를 해제하기 위함이다. 그리고 GC가 될 때까지 기다리면 Connection Pool이 부족해지는 것은 시간 문제다.
무엇보다도 가장 좋은 방법은 DB와 관련된 처리를 담당하는 관리 클래스를 만드는 것이다. 보통 DBManager라는 이름의 클래스를 많이 사용한다. Connection 객체도 JNDI를 찾아서 (lookup하여) 사용하는 DataSource를 이용하여 얻는다. 여기에 가장 처음에 배운 ServiceLocator 패턴까지 적용하면, DB 연결 시의 시간을 최소한으로 단축시킬 수 있다.
JDK 7에서 등장한 AutoClosable 인터페이스
JDK 7 이상을 사용할 때는 close() 메서드를 호출해야하는 대상이 AutoCloseable 인터페이스를 구현한 것인지 잘 확인하고 맞다면 try-with-resources를 사용하자.
JDBC를 사용하면서 유의할 만한 몇 가지 팁
- setAutoCommit() 메서드는 필요할 때만 사용하자.
- 자동 커밋 여부를 지정하는 작업은 반드시 필요할 때만 하자.
- 단순한 select 작업만을 수행할 때에도 커밋 여부를 지정하여 사용하는 경우가 많은데, 여러 개의 쿼리를 동시에 작업할 때 성능에 영향을 주게 되므로 되도록 자제하자.
- 배치성 작업은 executeBatch() 메서드를 사용하자.
- 배치성 작업을 할 때는 Statement 인터페이스에 정의되어 있는 addBatch() 메서드를 사용하여 쿼리를 지정하고, executeBatch() 메서드를 사용하여 쿼리를 수행하자.
- 여러 개의 쿼리를 한 번에 수행할 수 있기 때문에 JDBC 호출 횟수가 감소되어 성능이 좋아진다.
- setFetchSize() 메서드를 사용하여 데이터를 더 빠르게 가져오자.
- 한 번에 가져오는 열의 개수는 JDBC의 종류에 따라서 다를 것이다. 하지만 가져오는 데이터의 수가 정해져 있을 경우에는 Statement와 ResultSet 인터페이스에 있는 setFetchSize() 메서드를 사용하여 원하는 개수를 정의하자.
- 하지만 너무 많은 건수를 지정하면 서버에 많은 부하가 올 수 있으니, 적절하게 사용해야 한다.
- 한 건만 필요할 때는 한 건만 가져오자.
- 실제 쿼리에서는 100건 정도를 가져오는데, ResultSet.next()를 while 블록을 사용해서 수행하지 않고, 단 한 번만 메서드를 수행해 결과를 처리하는 경우가 있다.
- 이 경우에 단 한 건만을 가져오도록 쿼리를 수정해야 한다.
정리
- 성능 진단을 하면 80~90% 정도의 시스템이 DB에서 많은 시간이 소요된다.
- 하지만 나머지 10~20% 정도의 시스템은 DB 외적인 부분에서 많은 시간이 소요된다.
- DB에 소요되는 시간을 제외하면, DB를 처리하기 위한 시간이 그중 절반 정도를 점유한다.
- 적어도 여기서 설명된 내용만이라도 적용하면 더욱 빠른 처리 결과를 얻을 수 있을 것이다.
'Java' 카테고리의 다른 글
[자바 성능 튜닝 이야기] java 성능 튜닝 총정리 (0) | 2020.09.04 |
---|---|
[자바 성능 튜닝 이야기] JVM 구동 과정 / GC 튜닝 (0) | 2020.09.03 |
정보 은닉과 데이터 캡슐화는 동일한 개념이 아니다 (0) | 2020.05.06 |
CQS(Command-Query Separation) 란? (0) | 2020.05.05 |
일급 컬렉션의 장점 (0) | 2020.03.26 |