반응형
서론
이 글은 '주니어 백엔드 개발자가 반드시 알아야 할 실무 지식' 책을 읽고 진행하는 스터디 내용을 정리한다. 책을 읽고 학습하는 게 아니라, 책의 내용을 주제로 경험을 공유하는 스터디이기 때문에 포스팅 내용은 책과 관련이 없다. 이번 글은 '3장 성능을 좌우하는 DB 설계와 쿼리', '4장 외부 연동이 문제일 때 살펴봐야 할 것들'을 읽고 진행한 스터디 내용을 정리했다. (경험 공유) 표기는 스터디원들이 실무에서의 경험을 정리한 것이며, 항상 정답인 예제가 아니다.

3장. 성능을 좌우하는 DB 설계와 쿼리
선택도 (58p)
- 인덱스에서 특정 컬럼의 고유한 값 비율
- 선택도가 높다 = 고유값이 많다
커버링 인덱스 (59p)
- 인덱스 안에 포함된 데이터를 사용하므로 실제 데이터에 접근할 필요 없음.
- 쿼리를 충족시키는데 필요한 모든 데이터를 갖고 있는 인덱스
설문조사를 좋아요하는 CUD를 어떻게 개선할까? (63p)
- 설문조사와 설문조사를 좋아요 하는 집계 수를 join 해서 조회하는 쿼리가 있다고 가정. 이를 어떻게 개선하면 좋을까?
- 개선할 수 있는 방법
- 설문조사 테이블에 좋아요 집계 컬럼 추가
- 좋아요 CUD 발생 할때마다 설문조사의 집계 컬럼 변경
- 또는 배치로 정합성 맞추기
- 설문조사 조회 쿼리 / 좋아요 수 조회 쿼리를 분리 후 캐싱
- 현재 상태(join 쿼리) 유지
- 설문조사 테이블에 좋아요 집계 컬럼 추가
- (경험 공유) update가 너무 많은 케이스를 개선
- update가 많은 값 Redis에 적재
- 일정 주기마다 Redis 조회해서 DB 업뎃

delete 쿼리와 디스크 용량 (74p)
- delete 쿼리를 실행하면 삭제되었다는 표시만 남기고, 디스크 용량이 줄어들지는 않는다
- 삭제된 공간은 향후 재사용되나, 단편화 현상이 발생할 수 있다
- 데이터 추가/변경/삭제 과정에서 데이터가 흩어져 저장 → 빈 공간 생성
- 참고: https://dev.mysql.com/doc/refman/8.0/en/delete.html
MySQL :: MySQL 8.0 Reference Manual :: 15.2.2 DELETE Statement
DELETE is a DML statement that removes rows from a table. A DELETE statement can start with a WITH clause to define common table expressions accessible within the DELETE. See Section 15.2.20, “WITH (Common Table Expressions)”. Single-Table Syntax DELE
dev.mysql.com
DB Replica (75p)
- 우리 서버의 replica 개수 확인하는 법
- AWS > Aurora and RDS > Databases

쿼리 타임아웃 (77p)
- hikari의 max-life-time
- 히카리 풀에서 커넥션이 살아있을 수 있는 시간
- 이 값이 작으면 Aurora Failover 시 더 유연하게 작동
- Failover: 프라이머리 DB 인스턴스에 장애가 발생 시 자동으로 다른 인스턴스가 그 역할을 대신함
- max-life-time 값이 작으면 커넥션이 더 자주 만료되고 재생성
- 이전 프라이머리 인스턴스와의 연결이 끊어지고, 신규 프라이머리로 연결을 다시 수립
- DB wait_timeout보다 짧게 설정하는 것을 권장
- 네트워크 지연, 애플리케이션 성능 지연 고려
- DB wait_timeout이 더 작으면 DB서버는 커넥션 종료했지만, client app에서는 커넥션 살아있다고 판단 → 커넥션 누수 발생 가능
타임아웃 발생 시 수행되는 로직
- 상황마다 다른데 보통 예외를 그대로 전파
- (경험 공유) 조회 시 필수 값이 아니면 null이나 empty 처리
DB 최대 연결 개수 (85p)
- SHOW VARIABLES LIKE 'max_connections';
- 하나의 DB에 여러 서버가 붙으면 커넥션 고갈 문제가 있지 않을까?
- 디폴트 값은 151이지만 사내에서는 훨씬 크게 늘려서 사용 중 (디폴트 값 참고: MySQL 8.0 문서)
- 주의가 필요하나 (값을 크게 커스텀했기 때문에) 생각보다 자주 발생할 일은 아니라고 예상
(경험 공유) 슬로우 쿼리 개선
- 데이터 전체 조회로 인한 슬로우 쿼리
- 데이터가 많아질수록 실행시간 증가
- 인덱스 적용된 컬럼 기준으로 나눠서 조회
- 조회 조건이 많아 인덱스를 타지 않았던 이슈
- 인덱스가 존재하는 컬럼으로만 조회하도록 개선
- 그 외 필터링은 애플리케이션 로직에서 적용
- 조인 조회 쿼리 수행 시 varchar 컬럼의 캐릭터셋 불일치 이슈
- 쿼리 속도 늦어짐
- 트래픽이 적은 새벽 시간에 캐릭터셋 변경
- 타입 불일치로 인덱스 활용이 힘들었던 경험
- int형이어야 하는데 테이블에 varchar로 선언됨
- where 조건에서는 숫자지만 테이블은 varchar → 수행 시 타입 변환이 필요 → 리소스 낭비 & 인덱스도 안 탐
- 컬럼 타입 변경으로 해소 (데이터가 얼마 쌓여있지 않은 상황이라 가능했었음)
4장. 외부 연동이 문제일 때 살펴봐야 할 것들
타임아웃 (91p)
- (경험 공유) 모든 서버에서 자주 사용하는 플랫폼의 응답 지연
- 스노우볼 효과
- A → B → C 서버로의 의존성이 존재하고, 모든 서버에서 D 서버를 호출한다고 가정
- D 서버의 API가 지연 시 A 서버에서는 정말 긴 타임아웃 필요
- 후속 대응 - 외부 서버/API 별로 타임아웃 다르게 설정
- 스노우볼 효과
재시도 (96p)
- (경험 공유) 재시도 대신 이벤트 활용했던 이유
- 재시도 실패에 대한 처리 용이
- 재시도가 반드시 성공해야 하는 상황
- 변경 기능이 멱등하지 않은 케이스가 많음
- 조회용 외부 API 호출도 재시도가 필요할까?
- 아예 잘못된 요청인 경우 존재 (ex: request에 필수값이 null로 들어갔다)
- 일시적 오류는 보통 1~2초 유지되는 경우가 많아서 1~2초 내 재시도 시 유의미한 값을 받아오기 힘들 것 같음
동시 요청 제한 (100p)
- tomcat 스레드 200, 대기가능 100개면 301번째 요청은 어떻게 되는가?
- 연결을 거부하거나 타임아웃이 발생할 수 있다
- 참고: Apache Tomcat 9 문서 - acceptCount
서킷 브레이커 (102p)
- 빠른 실패 처리
- 불필요한 트래픽 전파 방지
- (경험 공유) 서킷 브레이커와 예외 처리
- 이벤트 중복 발행을 방지하기 위해 Redis에 중복 체크 정보를 저장
- 서킷 브레이커가 열리면 모든 이벤트를 중복이 아닌 것으로 처리
- 이벤트 발행이 더 중요한 도메인이기 때문
외부 연동과 DB 연동 (104p)
- 외부연동 / DB 연동 수행 실패했을 때 해야 할 일
| 외부연동 | DB연동 | 해야할 일 |
| O | X | 데이터 주기적으로 맞추기 |
| X | O | 외부 연동만 다시 시도 외부 연동 실패 응답하면 DB로직 수행도 수행하지 않도록 개선 |
| X | X | 재수행 |
| X read timeout | O | 데이터 주기적으로 맞추기 or 재수행 및 수행 시 이미 성공 완료된 건인지 확인 |
- insert → 외부 call → update 구조라면 어떻게 대응할 수 있을까?
- 외부 call 지연을 대비해 서킷 걸어서 빠르게 실패시킨다
- (경험 공유) 트랜잭션 범위를 작게 쓰자
- API 전체를 통으로 트랜잭션 감싸는 게 성능 측면에서도 좋지 않음
- 참고: https://tech.kakaopay.com/post/jpa-transactional-bri/#실제로-set_option과-commit이-성능에-영향을-미칠까
- (경험 공유) 외부/DB 데이터 불일치해도 당장 처리하지 않음
- 실시간이 중요하지 않음
- 원인이 다양해서 수동으로 확인 후 처리
JPA Transactional 잘 알고 쓰고 계신가요? | 카카오페이 기술 블로그
JPA Transactional과 그에 따른 DB 쿼리 성능과의 관계에 대해서 설명합니다.
tech.kakaopay.com
HTTP 커넥션 풀 (109p)
- 브라우저에는 자체 HTTP 커넥션 풀이 존재
- 서버-서버는 HttpClient, FeignClient 등의 클라이언트 라이브러리
- 커넥션 풀 크기
- 커넥션 가져오는 대기 시간
- 커넥션 유지 시간 (keep alive)
- (경험 공유) 외부 API 호출 타임아웃 이슈 개선 공유
- keep-alive 설정이 없을 때, 타임아웃이 자주 발생했었음
- HTTP 커넥션 유지 → 3 way handshake 안 해도 됨 → 응답 속도 빨라짐
반응형
'Develop > etc' 카테고리의 다른 글
| [백엔드 실무 스터디] 5, 6, 7. 비동기, 동시성, I/O 병목 제어 (4) | 2025.08.17 |
|---|---|
| [백엔드 실무 스터디] 2장. 느려진 서비스 살펴보기 (0) | 2025.07.05 |
| 구조적 동시성 이해하기 (feat. goto의 역사) (0) | 2024.12.23 |
| [Git] 머지 커밋 revert 하기 (5) | 2023.08.13 |
| Windows에서 8080 port 사용이 불가능한 현상 (1) | 2022.09.18 |