데이터베이스 튜닝에 대하여
728x90

튜닝은 데이터베이스 시스템의 성능을 끌어올리고 안정성과 신뢰성을 높이는 중요한 과정이다. 이를 통해, 쿼리 성능을 향상하고, 시스템 자원을 효율적으로 사용할 수 있다.

튜닝을 하는 이유는 주어진 하드웨어 환경을 통해서 처리량과 응답속도를 개선하기 위해서이다.

성능 저하가 일어나면 시스템의 안정성과 사용성은 떨어질 수밖에 없기에 주기적으로 데이터베이스 튜닝을 진행해야 한다.

 

튜닝이라고 하면 포괄적으로 생각할 수 있는데, 아래 방법들을 말한다.

  • 쿼리 최적화
  • 캐싱 전략
  • 하드웨어 자원 관리
  • 인덱스 관리
  • ...

 

이러한 튜닝을 진행할 때 단계가 있다.

1. 데이터베이스 설계 튜닝(모델링 관점)

  • 데이터 모델링, 인덱스 설계
  • 데이터베이스 용량 산정
  • ex) 반정규화

2. 데이터베이스 환경 튜닝(환경 관점)

  • 메모리나 블록 크기 지정
  • ex) Buffer, cache 크기 설정

3. SQL 문장 튜닝(APP 관점)

  • 효율적인 SQL 작성
  • ex) JOIN, INDEXING, SQL EXECUTION Plan 

 

일단 튜닝 방법에 대해 자세히 알아보겠다.

일단 쿼리 최적화부터 보자면, 잘 최적화된 쿼리는 더 적은 자원을 사용해 더 빠르게 결과를 반환할 수 있기 때문에 꼭 필요한 과정이다.

쿼리 최적화를 진행할 때는 먼저 `EXPLAIN` 이라는 키워드를 통해서 SQL 쿼리 실행 계획을 분석해야 한다.

EXPLAIN SELECT name, salary FROM employees WHERE hire_date > '2020-01-01';

위 쿼리를 실행하면, 데이터베이스가 쿼리를 처리하기 위해 사용하는 인덱스, 테이블 스캔 여부, 필터링 조건 등을 확인할 수 있다. 실행 계획을 분석하여 비효율적인 접근 방식을 개선하는 것이 중요하다.

이때 조건절을 최적화하여 성능을 개선할 수 있다. 예를 들어, 데이터 범위를 좁히는 조건을 먼저 적용하여 검색 범위를 줄이는 것이 효과적이다.

또한 인덱스를 통해서 성능을 개선할 수 있다.

인덱스를 통해서 테이블을 전체 스캔하지 않고 필요한 데이터만 빠르게 조회할 수 있다.

CREATE INDEX idx_hire_date ON employees(hire_date);

위 명령어는 employees 테이블의 hire_date 컬럼에 인덱스를 생성한다. 이후 해당 칼럼을 조건으로 사용하는 쿼리는 훨씬 빠르게 실행된다.

 

조건절 최적화와 인덱스를 사용해서 쿼리 최적화를 한 예시를 보겠다.

문제 상황

explain 키워드를 통해서 시스템에서 가장 오래 걸리는 쿼리를 확인했을 때, 1번 쿼리가 응답 시간이 길고 호출 횟수가 가장 많음이 확인되었다.

1번 쿼리:

SELECT code, Name FROM STUDENT WHERE Name IS NOT NULL;
  • 문제:
    • WHERE Name IS NOT NULL 조건은 부정 연산자(NOT)를 사용하고 있어, 인덱스 활용이 불가능하다.
    • 이로 인해 전체 테이블 스캔이 발생하여 응답 시간이 길어졌다.

해결 방법:

NOT과 같은 부정 연산자 대신, 비교 연산자(>=)를 사용하여 인덱스를 활용하도록 개선하였다.

SELECT code, Name FROM STUDENT WHERE Name >= '';
  • 부정 연산자(NOT, !=, <>)는 일반적으로 인덱스를 사용할 수 없기 때문에 성능이 저하된다.
  • 반면, 비교 연산자(>=)는 인덱스를 사용할 수 있어 쿼리 성능을 크게 향상할 수 있다.

 

하지만 인덱스를 무조건 사용하는 것이 좋은 것은 아니다! (주의)

  • 인덱스를 생성할 때는 자주 조회되는 컬럼이나, 조건으로 자주 사용되는 칼럼에 인덱스를 생성해야 한다.
  • 너무 많은 인덱스는 데이터 삽입, 삭제, 갱신 시 성능을 저하시킬 수 있기 때문에 불필요한 인덱스는 제거해야 한다.
  • 그래서 주기적으로 인덱스를 재구성하거나 다시 빌드하는 것이 필요하다.
ALTER INDEX idx_hire_date REBUILD;

 

다음으로 캐싱 전략에 대해 살펴보겠다.

캐싱은 자주 조회되는 데이터를 메모리에 저장하여 데이터베이스의 부하를 줄이고, 조회 성능을 크게 향상시키는 기술이다. 메모리에서 데이터를 조회하는 것은 디스크에서 데이터를 읽는 것보다 훨씬 빠르다.

캐시 만료와 크기 관리에 신경써야 한다.

  • 오래된 데이터를 제거하고 최신 데이터를 유지하려면 적절한 만료 시간을 설정해야 한다.
  • 캐시 크기가 너무 크면 메모리 사용량이 증가하고, 너무 작으면 캐시 히트율이 낮아질 수 있다.

 

또한 데이터베이스 성능은 하드웨어 자원에 크게 좌우되기에, 하드웨어 자원을 모니터링하여 적절히 관리하는 것이 중요하다.


이때 스프링부트로 개발을 하면 데이터베이스 튜닝을 고려할 때 JPA도 같이 고려해야 할 것 같아서 추가적으로 내용을 작성하려고 한다.

JPA는 자바 애플리케이션과 데이터베이스와의 상호작용을 단순화한 ORM -> Object-Relational Mapping 기술이다. JPA를 통해서 SQL 쿼리를 직접 작성하지 않고 데이터베이스 작업을 수행할 수 있다.

 

JPA 구성 요소

  1. 엔티티(Entity): 데이터베이스 테이블과 매핑되는 자바 클래스.
  2. 엔티티 매니저(Entity Manager): 엔티티의 생명 주기를 관리하는 역할을 담당.
  3. JPQL(Java Persistence Query Language): SQL과 유사하지만 엔티티 객체를 대상으로 쿼리를 작성하는 언어.

 

JPA 구성요소에서 엔티티에서는 인덱스를 설정해 검색 성능을 향상할 수 있다.

JPQL 쿼리에서는 필요한 데이터만 조회하도록 쿼리를 작성한다면 쿼리를 최적화할 수 있다.

또한 Hibernate의 2차 캐시를 활용해서 자주 조회되는 데이터를 캐시에 저장할 수 있다. (@Cacheable 애노테이션 사용)

그리고 대량의 데이터를 처리할 때는 배치 처리를 사용하여 성능을 개선할 수 있다. (entityManager.merge)

 

개발을 진행할 때 데이터베이스 튜닝과 JPA 최적화는 애플리케이션 성능을 향상시킬 수 있는 중요한 요소들이다. 그렇기 때문에 해당 요소들을 고려하여 개발해야 할 것이다.

728x90
반응형