Skip to content

Commit 8f7d901

Browse files
authored
docs Update README with transaction and query optimization details
Refactor transaction propagation and optimization sections in README.md with detailed explanations on transaction properties, improvements, and query performance optimization techniques.
1 parent d2ed0f0 commit 8f7d901

1 file changed

Lines changed: 66 additions & 19 deletions

File tree

README.md

Lines changed: 66 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
## 트랜잭션 전파 속성
1+
<details>
2+
<summary><h1> 트랜잭션 전파 속성</h1></summary>
3+
24
### REQUIRED
35
: 스프링 트랜잭션의 기본 설정 이미 진행 중인 트랜잭션이 있으면 기존 트랜잭션에 합류하고, 없으면 새로운 물리 트랜잭션을 생성함 여러 논리 트랜잭션이 하나의 물리 트랜잭션을 공유하게 됨
46

@@ -328,7 +330,10 @@ processOrder 시작 (신규 물리 트랜잭션 1 시작)
328330

329331
REQUIRES_NEW보다 DB Connection 낭비를 줄이면서, 부분 롤백(특정 작업 실패 시 메인 작업은 유지)이 필요할 때 사용 (단, JPA에서는 지원하지 않으며 JDBC Savepoint 기능에 의존)
330332

331-
## 트랜잭션 개선 사항
333+
</details>
334+
335+
<details>
336+
<summary><h1>트랜잭션 개선 사항</h1></summary>
332337

333338
이번 리팩토링에서는 결제/취소/만료 스케줄러 흐름을 중심으로 트랜잭션 경계를 다시 정리함
334339
목표는 다음 3가지 :
@@ -473,11 +478,59 @@ REQUIRES_NEW보다 DB Connection 낭비를 줄이면서, 부분 롤백(특정
473478
- 보상 처리의 동시성 안정성이 높아짐
474479
- 결제 상태 전이 규칙이 강화되어 잘못된 상태 변경 가능성이 줄어듦
475480

476-
---
477-
## 쿼리 성능 최적화
481+
</details>
482+
483+
<details>
484+
<summary><h1>인덱스 종류 조사</h1></summary>
485+
486+
### 클러스터링 인덱스 (
487+
488+
* **개념:** 테이블의 실제 데이터가 인덱스의 정렬 순서대로 물리적으로 저장되는 방식 (국어사전과 같은 구조)
489+
* **특징:**
490+
* 테이블당 딱 1개만 생성 가능함.
491+
* 보통 기본키(Primary Key)에 자동 적용됨.
492+
493+
* **적용 지점:** `BETWEEN`, `>`, `<` 등 범위 검색(Range Query)이 잦은 컬럼. `ORDER BY``GROUP BY`가 자주 발생하는 기준 컬럼에 매우 유리함.
494+
495+
### 논클러스터링 인덱스 / 세컨더리 인덱스
496+
497+
* **개념:** 물리적 데이터 정렬과 무관하게 논리적인 인덱스(색인) 페이지만 따로 만들어두고, 실제 데이터가 있는 주소값을 저장하는 방식 (전공 서적 맨 뒤의 찾아보기와 같은 구조)
498+
* **특징:**
499+
* 테이블당 여러 개 생성 가능함.
500+
* 인덱스에서 위치를 찾은 뒤, 실제 테이블 데이터로 한 번 더 접근(Look-up)하는 과정이 필요함.
501+
502+
* **적용 지점:** `WHERE` 절의 조건이나 `JOIN`에 자주 사용되지만, 데이터 변경(INSERT, UPDATE, DELETE)이 너무 빈번하지 않은 컬럼에 적합함.
503+
504+
### 커버링 인덱스
505+
506+
* **개념:** 실행하려는 쿼리를 충족시키는 데 필요한 '모든 컬럼'을 이미 다 가지고 있는 인덱스
507+
* **특징:**
508+
* 특정 인덱스의 종류라기보다는, **쿼리의 모든 항목이 인덱스에 포함된 상태**를 뜻함.
509+
* 실제 데이터 블록(테이블)에 접근하는 Look-up 과정 없이 인덱스 페이지만 읽고 바로 결과를 반환하므로 성능이 압도적으로 빠름.
510+
511+
* **적용 지점:** 무한 스크롤이나 게시판 페이징 등 데이터베이스 부하가 심각한 쿼리를 튜닝할 때. `SELECT` 절에 가져와야 할 컬럼 수가 적고, 검색 조건이 명확한 경우 매우 효과적임.
512+
513+
### 복합 인덱스
478514

479-
이번 과제에서는 실제 서비스에서 자주 사용되거나 성능 병목이 발생할 수 있는 쿼리를 선정한 뒤,
480-
`EXPLAIN ANALYZE`를 통해 실행 계획을 직접 확인하고 인덱스를 개선하여 성능을 최적화했다.
515+
* **개념:** 2개 이상의 컬럼을 결합하여 하나로 만든 인덱스
516+
* **특징:**
517+
* 인덱스를 구성하는 **컬럼의 배치 순서**가 성능을 좌우함. (첫 번째 컬럼 기준으로 먼저 정렬된 후, 그 안에서 두 번째 컬럼이 정렬되는 방식)
518+
519+
* **적용 지점:** 항상 2개 이상의 컬럼이 `WHERE` 조건에서 `AND`로 같이 묶여서 검색될 때. (예: `WHERE status = 'ACTIVE' AND created_at > ...` 형태의 쿼리가 핵심 비즈니스 로직일 때 적용함)
520+
521+
### 유니크 인덱스
522+
523+
* **개념:** 중복된 값을 절대 허용하지 않는 인덱스
524+
* **특징:**
525+
* 내부적인 트리 구조는 세컨더리 인덱스와 동일함.
526+
* 데이터 삽입/수정 시 중복 체크 과정이 추가되므로 쓰기 성능은 약간 떨어질 수 있음.
527+
528+
* **적용 지점:** 유저의 이메일, 닉네임 등 비즈니스 로직상 유일성이 반드시 보장되어야 하면서, 동시에 검색 조건으로 자주 사용되는 컬럼에 적용함.
529+
530+
</details>
531+
532+
<details>
533+
<summary><h1>쿼리 성능 최적화</h1></summary>
481534

482535
최적화 대상은 다음 3가지 쿼리이다.
483536

@@ -652,28 +705,19 @@ Covering index range scan on ms using idx_movie_screen_screen_start_end
652705

653706
---
654707

655-
## 실행 계획 및 성능 비교 요약
708+
## 실행 계획 및 성능 비교
656709

657710
| 쿼리 | 기존 방식 | 개선 방식 | 최적화 전 | 최적화 후 |
658711
|---|---|---|---:|---:|
659712
| 예약 만료 대상 조회 | `status`, `created_at` 인덱스 + `payment_status` 후처리 | `status`, `payment_status`, `created_at`, `id` 복합 인덱스 | 실행시간 차이 미미 | `Covering index range scan` 확인 |
660713
| 사용자별 예매 목록 조회 | `user_id` 인덱스 탐색 후 정렬 | `user_id`, `created_at`, `id` 복합 인덱스 | `2.57 ms` | `0.665 ms` |
661714
| 상영 시간 겹침 검사 | `screen_id` 인덱스 탐색 후 시간 필터 | `screen_id`, `start_at`, `end_at` 복합 인덱스 | `0.124 ms` | `0.052 ms` |
662715

663-
---
664-
665-
## 결론
666-
667-
이번 최적화에서는 단순히 인덱스를 추가하는 데 그치지 않고,
668-
실제 쿼리의 조건 순서와 접근 패턴에 맞게 복합 인덱스를 설계했다.
669-
670-
특히 다음과 같은 점을 확인할 수 있었다.
671-
672-
- 조건 컬럼이 인덱스에 충분히 반영되지 않으면 후처리 `Filter`가 남는다.
673-
- `WHERE + ORDER BY + LIMIT` 패턴은 정렬 컬럼까지 포함한 복합 인덱스가 매우 중요하다.
674-
- `Covering index scan`이 가능해지면 테이블 본문 접근 없이 더 효율적인 조회가 가능하다.
716+
</details>
675717

676718

719+
<details>
720+
<summary><h1>로깅과 캐싱</h1></summary>
677721

678722
## 운영 로깅 개선
679723

@@ -716,6 +760,8 @@ Covering index range scan on ms using idx_movie_screen_screen_start_end
716760
- `AdminMovieService#createTheater()`: 극장 생성 후 해당 지역 목록이 달라지므로 `theatersByRegion` 캐시 무효화
717761
- `AdminMovieService#createSchedule()`: 시간표 등록 후 같은 극장/날짜 조회 결과가 달라지므로 해당 `schedules` 캐시 무효화
718762
- `ReviewService#createReview()`: 리뷰 작성 후 리뷰 목록과 영화 평점 정보가 달라질 수 있어서 `movieReviews`, `movieDetail` 캐시 무효화
763+
</details>
764+
719765

720766
<details>
721767
<summary><h1>❓</h1></summary>
@@ -885,6 +931,7 @@ JOIN m.team t -- Member 엔티티 내부의 team 필드(연관관계) 기준
885931
- **해결 :** 1. 컬렉션 타입을 `List` 대신 `Set`으로 변경함 (단, 데이터 순서가 보장되지 않고 여전히 카테시안 곱 문제는 존재)
886932

887933
2. 가장 권장되는 방법은 하나만 페치 조인하고 나머지는 **`Batch Size`** 설정을 통해 여러 번의 쿼리로 나누어 가져오는 것
934+
888935
</details>
889936

890937

0 commit comments

Comments
 (0)