분류
개발 · 3쪽
DB·백엔드에서 실제로 터지는 문제와 USB·CD 굽기 교과서를 모았다. 느린 쿼리, 락과 트랜잭션, 장애와 확장, 배포와 관측은 태그로 나눠 찾는다.
96편 · 글 80 · 강 16 · 마지막 글 2026.09.28
교과서굽기 16강
개발 글
96편 중 25–36
트래픽 몰릴 때만 느린 정렬, work_mem 이 곱해진다
정렬이 들어간 쿼리가 평소엔 빠른데 트래픽이 몰리는 시간에만 수십 배 느려진다. 혼자 재현하면 멀쩡하다. 메모리 설정을 크게 잡으면 낫겠다 싶지만, 그러다 서버가 죽기도 한다.
인덱스에 다 넣었는데 PostgreSQL 이 테이블을 또 읽는다
필요한 컬럼을 전부 인덱스에 넣었는데 PostgreSQL 이 여전히 테이블을 읽는다. 대량 갱신 직후엔 느리다가 한참 지나면 저절로 빨라진다. 그 「한참」 사이에 VACUUM 이 하는 일이 있다.
LIMIT 10 은 0.1초인데 LIMIT 100 은 왜 12초일까
LIMIT 10 을 붙였더니 오히려 느려졌다. LIMIT 10 은 0.1초인데 LIMIT 100 은 12초가 걸리기도 한다. 옵티마이저가 LIMIT 값을 보고 계획을 통째로 바꿨다.
EXPLAIN ANALYZE 는 추정이 빗나간 곳을 보라고 있다
느린 쿼리를 잡으려고 EXPLAIN 을 열어도 숫자가 무슨 뜻인지 모르겠다. 인덱스 스캔인데도 느리면 더 막막하다. 먼저 볼 건 추정 행 수와 실제 행 수다.
readOnly 를 안 붙이면 읽기 복제본이 논다
읽기 복제본을 붙였는데 주 서버 부하가 그대로였다. 라우팅 설정도 맞았다. 코드에 SELECT 만 있어도 읽기 전용이라고 선언하지 않으면 쓰기로 취급된다.
백만 건 배치를 트랜잭션 하나로 감쌌더니 두 시간이 날아갔다
백만 건 야간 배치를 트랜잭션 하나로 감쌌다. 도는 두 시간 동안 서비스가 느려졌고, 중간에 실패하면 처음부터 다시 두 시간이었다.
낙관적 락이냐 비관적 락이냐, 충돌률이 정한다
낙관적 잠금은 빠르고 비관적 잠금은 안전하다는 말, 둘 다 맞게 들려서 더 헷갈린다. 실제로는 충돌이 얼마나 자주 나느냐에 따라 유불리가 뒤집힌다.
따로 보이던 장애 셋, 범인은 긴 트랜잭션 하나였다
커넥션 부족, 테이블 팽창, 락 경합이 한꺼번에 났다. 셋을 하나씩 따로 조사했는데 알고 보니 원인은 하나였다. 트랜잭션이 길다는 것.
알림은 왔는데 주문이 없다, 아웃박스 패턴
주문을 저장하고 바로 알림 이벤트를 보냈다. 가끔 알림 서비스가 「그런 주문 없는데요」라고 했다. 취소된 주문 알림이 나가기도 했다. 순서를 바꿔도 안 풀린다.
워커를 늘려도 그대로인 DB 큐, SKIP LOCKED 로 푼다
DB 테이블로 만든 작업 큐에 워커를 다섯으로 늘렸다. 처리량은 한 대일 때와 똑같았다. 다섯이 전부 맨 앞 작업 하나 앞에 줄을 서고 있었다.
CPU 도 DB 도 한가한데 앱이 멈췄다, REQUIRES_NEW
로그만은 남기려고 저장 메서드를 별도 트랜잭션으로 뺐다. 그런데 트래픽이 오르자 CPU 도 DB 도 한가한데 앱이 멈췄다. 요청 하나가 커넥션 둘을 쥐고 있었다.
@Transactional 을 붙였는데 롤백이 안 된다
트랜잭션 애너테이션을 붙였는데 예외가 나도 롤백이 안 된다. 오타도 없고 임포트도 맞다. 로그를 켜 보면 트랜잭션이 아예 시작되지 않았다.
조건에 맞는 글이 없다. 브루니가 숨었나 보다.