diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5f1c671..58b3679 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,7 +2,7 @@ name: CI/CD Deploy to EC2 on: push: - branches: [ "develop", "rollback" ] + branches: [ "develop" ] permissions: contents: read diff --git a/README.md b/README.md index d81583a..dc67d75 100644 --- a/README.md +++ b/README.md @@ -1579,4 +1579,334 @@ export const options = { - Prometheus에서 spring container에 대한 부하를 탐색하고 싶었지만 하지 못하였다... -![prometheus.png](images/prometheus.png) \ No newline at end of file +![prometheus.png](images/prometheus.png) + +--- +# 7주차 + +## 트랜잭션 전파(Propagation) + +트랜잭션이 이미 진행 중일 때, 새로 호출된 메서드가 기존 트랜잭션에 참여할지, 새로운 트랜잭션을 생성할지를 결정하는 설정 + +전파는 논리 트랜잭션과 물리 트랜잭션의 처리 방식에 영향을 줌 + +- 물리 트랜잭션: 실제 DB Connection 단에서 커밋/롤백되는 트랜잭션 +- 논리 트랜잭션: 스프링 내부에서 메서드 단위로 관리하는 트랜잭션 경계 + +### 원칙 + +- 모든 논리 트랜잭션이 커밋되어야 물리 트랜잭션이 커밋됨 +- 하나라도 롤백하면 물리 트랜잭션은 롤백됨 +- 신규 트랜잭션만 물리 트랜잭션을 커밋 또는 롤백할 수 있음 + +--- + +### REQUIRED (기본값) + +- 트랜잭션이 있으면 기존 트랜잭션에 참여하고, 없으면 새로운 트랜잭션을 만듦 + +- 서비스 간 호출 시 가장 일반적으로 사용되는 방식 + +--- + +### REQUIRES_NEW + +- 기존 트랜잭션이 있으면 일시 정지시키고, 항상 새로운 물리 트랜잭션을 시작함 + +- 로그 기록처럼, 메인 로직이 실패해도 별도로 커밋되어야 하는 경우에 사용됨 + +--- + +### SUPPORTS + +- 트랜잭션이 있으면 참여하고, 없으면 트랜잭션 없이 실행함 + +- 트랜잭션이 반드시 필요하지 않은 조회성 메서드에 적합함 + +--- + +### MANDATORY + +- 기존 트랜잭션이 반드시 존재해야 함 + +- 트랜잭션 없이 호출하면 예외(IllegalTransactionStateException)가 발생함 + +- 상위 트랜잭션 내부에서만 실행되어야 하는 경우에 사용함 + +--- + +### NOT_SUPPORTED + +- 트랜잭션을 사용하지 않음 + +- 만약 기존 트랜잭션이 존재한다면 일시 정지시키고 non-transactional로 실행함 + +- 트랜잭션에 묶이면 안 되는 로직(외부 호출, 로그 저장)에 사용됨 + +--- + +### NEVER + +- 트랜잭션이 존재하면 안 됨 + +- 트랜잭션이 있으면 예외가 발생 + +- 항상 트랜잭션 없이 실행되어야 하는 메서드에 사용됨 + +--- + +### NESTED + +- 상위 트랜잭션 내부에서 savepoint를 생성하여 부분 롤백이 가능한 중첩 트랜잭션을 만듦 + +- 상위 트랜잭션은 하나의 물리 트랜잭션을 유지하지만, 내부적으로 savepoint 기반으로 논리적 분리 지점을 만듦 + +- 상위 트랜잭션이 롤백되면 중첩 트랜잭션도 모두 롤백되지만, 중첩 트랜잭션만 실패하면 savepoint까지만 롤백함 + +참고: REQUIRES_NEW는 새로운 물리 트랜잭션을 만들지만, NESTED는 같은 물리 트랜잭션 내에서 savepoint로 관리한다. + +--- + +## Index + +데이터를 빠르게 조회하기 위해 특정 컬럼을 기준으로 미리 정렬해둔 자료구조 + +- 일반적으로 B-Tree 기반으로 구성되며, 검색 성능을 크게 향상시킴 + +--- + +## Index 종류 + +### 클러스터드 인덱스 (Clustered Index) + +- 테이블의 데이터 자체가 인덱스 순서대로 정렬되어 있는 구조 + +- MySQL(InnoDB)에서는 PK가 자동으로 클러스터드 인덱스로 사용됨 + +특징 + +- 한 테이블에 하나만 존재 +- 기본 키 순서대로 실제 데이터가 저장됨 +- 범위 조회에 매우 빠름 +- PK 변경이 많으면 비효율적 + +예시 + +- PK(id)를 기준으로 레코드가 디스크에 물리적으로 정렬됨 + +--- + +### 보조 인덱스 (Secondary Index, Non-Clustered Index) + +- 클러스터드 인덱스를 제외한 나머지 인덱스 + +- 실제 데이터가 아니라, 정렬된 인덱스 키와 PK 값을 저장한다. + +특징 + +- 테이블에 여러 개 생성 가능 +- 인덱스를 통해 PK로 이동한 뒤 실데이터를 읽음 (두 번 접근: 인덱스 → 테이블) +- 조회 속도는 빠르지만 쓰기 비용 증가 + +--- + +### 복합 인덱스 (Composite Index) +: 여러 개의 컬럼을 묶어서 하나의 인덱스로 만드는 방식 + +Ex) (title, director) + +특징 + +- 왼쪽부터 사용해야 인덱스가 효율적으로 동작한다 (Leftmost Prefix Rule) +- WHERE 절 조건 순서가 인덱스 구성과 다르면 인덱스가 사용되지 않을 수 있음 +- 데이터 중복도가 낮은 컬럼을 앞에 두는 것이 일반적으로 유리 + +예시 + +(title, director) 인덱스가 있을 경우 + +- title 단독 조회 → 인덱스 사용 가능 +- director 단독 조회 → 사용 불가능 +- title + director 조합 조회 → 사용 가능 + +--- + +### 커버링 인덱스 (Covering Index) + +: 쿼리가 필요한 모든 컬럼이 인덱스에 포함되어 있어 테이블을 전혀 접근하지 않아도 되는 인덱스 + +Ex) + +인덱스: (email, name) + +쿼리: + +``` +SELECT name FROM users WHERE email = 'test@test.com' + +``` + +→ 인덱스만 조회하고 테이블을 읽지 않아도 된다. + +특징 + +- 테이블 I/O를 제거하여 매우 빠름 +- 조회량 많은 대형 서비스에서 자주 활용됨 + +--- + +## 성능 최적화 + +- 영화 조회 API 이용 +- 영화 307개 INSERT + +```jsx +EXPLAIN +SELECT * FROM movies +ORDER BY booking_rate DESC +LIMIT 10; +``` + +1-1 + +- type : ALL + - Full Table Scan 수행 +- possible_keys : NULL + - 사용할 수 있는 인덱스 없음 +- key : NULL + - 실제 사용된 인덱스 없음 +- filtered = 100 + - 필터링 비율 100% 예측 + +```jsx +-> Limit: 10 row(s) (cost=31.7 rows=10) (actual time=0.532..0.537 rows=10 loops=1) + -> Sort: movies.booking_rate DESC, limit input to 10 row(s) per chunk (cost=31.7 rows=307) (actual time=0.531..0.536 rows=10 loops=1) + -> Table scan on movies (cost=31.7 rows=307) (actual time=0.0286..0.359 rows=307 loops=1) + +``` + +- actual time=0.532..0.537 + - 걸린 시간 0.537ms +- rows=10 + - 조회 예측 행 수 10개 + +```jsx +CREATE INDEX idx_booking_rate ON movies(booking_rate); +``` + +- booking_rate 단일 인덱스 적용 + +```jsx +-> Limit: 10 row(s) (cost=31.7 rows=10) (actual time=0.439..0.444 rows=10 loops=1) + -> Sort: movies.booking_rate DESC, limit input to 10 row(s) per chunk (cost=31.7 rows=307) (actual time=0.438..0.443 rows=10 loops=1) + -> Table scan on movies (cost=31.7 rows=307) (actual time=0.029..0.304 rows=307 loops=1) + +``` + +- actual time=0.439..0.444 + - 걸린 시간 0.444ms +- rows=10 + - 조회한 행 수 10개 + +```jsx +EXPLAIN +SELECT * FROM movies WHERE age_rating = 'AGE19'; +``` + +quert2-1 + +- type : ALL + - Full Table Scan 수행 +- filtered = 20 + - 필터링 비율 20% 예측 + +2-2 + +- actual time=0.027 .. 0.311 + - 걸린 시간 0.311ms +- rows=307 + - 조회한 행 수 307개 (ALL) + +### 쿼리 최적화 + +```jsx +CREATE INDEX idx_age_rating ON movies(age_rating); +``` + +- index age_rating에 생성 + +### 결과 + +2-3 + +- type: ref + - 동등 조건(=)을 인덱스로 조회할 때 나오는 타입 +- rows=47 +- filtered = 100 + - 예상 필터링 비율 100% row + +2-4 + +- actual time=0.0258..0.144 + - 걸린 시간 0.144ms +- rows=47 + - 조회한 행 수 47개 + +--- + +```jsx +EXPLAIN +SELECT * FROM movies +WHERE age_rating = 'ALL' AND runtime < 120; +``` + +explain age + +- type : ALL + - Full Table Scan 수행 +- possible_keys : NULL + - 사용할 수 있는 인덱스 없음 +- key : NULL + - 실제 사용된 인덱스 없음 +- rows=307 + - 조회 예측 행 수 307개 (ALL) +- filtered = 6.67 + - 필터링 비율 예측: 약 6.67% +- Extra = Using where + - 필터 조건(age_rating + runtime)을 통해 추가 필터링 적용 + +explain analyze age + +```jsx +> Filter: ((movies.age_rating = 'ALL') and (movies.runtime < 120)) (cost=31.7 rows=20.5) (actual time=0.0348..0.323 rows=20 loops=1) +-> Table scan on movies (cost=31.7 rows=307) (actual time=0.0268..0.274 rows=307 loops=1) +``` + +- actual time=0.0348..0.323 +- rows=307 + - 조회 예측 행 수 307개 (ALL) + +```jsx +CREATE INDEX idx_movies_age_runtime +ON movies (age_rating, runtime); +``` + +- 복합 인덱스 적용 +- (age_rating, runtime) + +결과 + +explain analyze age and runtime + +```jsx +-> Index range scan on movies using idx_movies_age_runtime over (age_rating = 'ALL' AND runtime < 120), + with index condition: ((movies.age_rating = 'ALL') and (movies.runtime < 120)) (cost=9.26 rows=20) (actual time=0.0355..0.12 rows=20 loops=1) + +``` + + +- actual time=0.0355..0.12 +- rows=20 + +--- +