백엔드를 개발하는 사람이 고민하는 부분 중, 중요한 부분인 데이터베이스에 접근하여 원하는 값을 찾으려 할 때의 성능 최적화가 있습니다. 오늘은 이런 DB 조회할 때의 성능 최적화에 대한 다양한 방법이나 아이디어에 대한 내용을 말해보겠습니다.
1. DB 조회 성능 최적화 방법 🔍
1. 적절한 조회방식 선택
데이터를 API가 필요한 조건에 맞춰서 정확히 지정하여 조회하는 것이 효율적입니다. 다음은 여러가지 DB를 조회하는 기술들에 대한 간략한 설명입니다.
- JPQL/Criteria API: JPA를 활용할 때 효율적인 쿼리 작성으로 필요한 데이터만 조회.
- Native Query: 복잡한 쿼리나 대량 데이터 처리 시 SQL을 직접 사용하여 최적화.
- QueryDSL: 동적 쿼리 작성 시 성능과 코드 가독성 모두 확보.
2. 불필요한 데이터 조회 최소화
원하는 데이터를 지정하고 조회를 하려고 해도, 부수적으로 조회될 수 밖에 없는 경우도 있습니다. 이런 부분을 방지하여 성능 최적화를 이룰수 있습니다.
- Lazy Loading 사용: 연관된 엔티티를 필요할 때만 로딩 (@OneToMany, @ManyToOne 등).
- Projection 사용: DTO나 특정 필드만 가져오는 Projection을 통해 필요한 데이터만 조회.
- Pagination: 대량 데이터를 처리할 때는 페이징 처리 (Pageable, Page).
특히 QueryDSL 을 사용하여 쿼리 작성시 Projection 기능을 사용하면 특정필드만 골라서 조회가능 하기 때문에 성능을 더 끌어올릴 수 있습니다.
3. 캐싱 활용
캐시 메모리에 자주 사용되는 데이터가 들어있게 하는 등의 방법을 통해 DB 까지 조회에 도달하지 않고 값을 불러오는 방법도 있습니다. 이 부분은 `캐싱` 이라는 키워드로 따로 블로그 포스팅을 다뤄볼 예정입니다.
- 1차 캐시: JPA에서 제공하는 기본 캐시를 활용 (영속성 컨텍스트).
- 2차 캐시: Ehcache, Redis 등을 사용하여 반복 조회 데이터 캐싱.
4. 쿼리 튜닝 및 인덱스 설정
- 조인 최적화: 불필요한 조인을 줄이고 필요한 데이터만 가져오기.
- 쿼리 단순화: 서브쿼리 대신 조인 사용, 불필요한 조건 제거.
- 자주 조회되는 컬럼, 검색 조건에 포함되는 컬럼에 인덱스를 설정(단, 과도한 인덱스는 추가적인 쓰기 비용을 초래할 수 있음)
2. 인덱싱이랑 무엇인가? 📃
인덱스(Index)는 DB에서 특정 컬럼에 대한 빠른 검색을 위해 사용되는 데이터 구조입니다. 책의 목차처럼 테이블에서 특정 데이터를 빠르게 찾아주는 역할을 합니다.
인덱스 종류
- Single Column Index: 단일 컬럼 기준의 인덱스.
- Composite Index: 여러 컬럼 조합으로 생성된 인덱스.
- Unique Index: 유일성을 보장하는 인덱스.
인덱스 생성 방법
MySQL 예제
CREATE INDEX idx_column_name ON table_name(column_name);
JPA 예제(Entity 에 설정)
@Entity
@Table(name = "table_name", indexes = @Index(name = "idx_column_name", columnList = "column_name"))
public class ExampleEntity {
@Id
private Long id;
private String columnName;
}
3. 성능 분석 및 최적화 확인 방법🔬
1. 실행 시간 측정
- SQL 쿼리 실행 시간 측정을 위해 데이터베이스 프로파일러나 로깅 툴 사용
- Spring Boot 에서 Hibernate 쿼리 로깅(application.properties)
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.generate_statistics=true
2. `EXPLAIN` 키워드 사용
- EXPLAIN 을 쿼리문 앞에 붙이면 쿼리 실행 계획을 확인할 수 있습니다.
- 실행 계획은 쿼리가 테이블에서 데이터를 검색하는 방식(스캔 방식, 인덱스 사용여부 등)을 보여줍니다.
EXPLAIN SELECT * FROM table_name WHERE column_name = 'value';
- 주요 필드
- type: 쿼리 수행 방식 (e.g., ALL, index, ref, const 등).
- key: 사용된 인덱스 이름.
- rows: 예상 처리 행 수.
- extra: 추가 정보 (e.g., Using index, Using temporary 등).
3. 쿼리 성능 비교
- 인덱스 추가 전/후로 EXPLAIN 결과에서 rows 값 감소 및 type 개선 확인.
- 실행 시간을 직접 측정하여 최적화된 쿼리 실행 확인.
4. `EXPLAIN` 결과 예시
인덱스가 없는 경우
id: 1
select_type: SIMPLE
table: table_name
type: ALL
possible_keys: NULL
key: NULL
rows: 10000
extra: Using where
- type: ALL -> 풀 테이블 스캔 발생
- rows: 10,000 -> 모든 행을 검사
인덱스가 있는경우
id: 1
select_type: SIMPLE
table: table_name
type: ref
possible_keys: idx_column_name
key: idx_column_name
rows: 100
extra: Using index
- type: ref -> 인덱스를 사용하여 효율적 조회
- rows: 100 -> 인덱스를 통해 대상 데이터만 조회
추가적인 팁
- 복합 인덱스를 사용할 경우, 검색 조건의 순서가 인덱스 정의 순서와 일치해야 효과적입니다.
- 인덱스를 과도하게 생성하면 쓰기 작업 성능이 저하될 수 있으니 꼭 필요한 경우에만 생성하는게 좋습니다.
- 데이터베이스와 애플리케이션 레벨에서 성능 로그를 분석하여 최적화 지점을 찾아볼 필요가 있습니다.
'Spring' 카테고리의 다른 글
Spring Framework 와 Spring Boot (1) (0) | 2025.03.30 |
---|---|
Spring - Redis의 Pub/Sub 및 WebSocket 구현하기 (4) | 2024.12.17 |
Spring - Redis 적용 실습 (0) | 2024.11.26 |
Spring - 낙관적 락 & 비관적 락 (0) | 2024.11.24 |
Spring - Redis (0) | 2024.11.22 |