개발 공부 35

개발 공부/DB [컬럼형 DB 파헤치기] 3편 - ClickHouse 외 다른 선택지들

시작하기 전에2편에서 메트릭 데이터를 MySQL과 ClickHouse에 똑같이 넣고 비교했을 때, 컬럼형인 ClickHouse가 대부분의 쿼리에서 크게 앞섰다.그런데 대규모 메트릭 데이터를 효과적으로 처리하는 방법이 이 둘만 있는 건 아니다. 이미 쓰던 MySQL에 사전집계를 더해볼 수도 있고, 시계열 전용으로 나온 TimescaleDB도 있고, Cassandra나 Druid를 떠올릴 수도 있다. 2편을 마무리하면서 이 선택지들이 계속 걸렸다. 같은 문제를 이 DB들로 풀면 어떻게 다를까 싶었다.그래서 3편에서는 2편과 같은 메트릭 수집 시나리오를 그대로 두고, 네 가지 방식을 하나씩 정리해봤다. 먼저 각각이 어떤 DB인지 개념을 살펴보고, 그중 직접 띄워볼 수 있는 두 가지(MySQL + 사전집계, T..

2026.06.28

개발 공부/DB MySQL에서 JSON 컬럼 써도 되나요?

프로젝트를 하다 보면 Map이나 Set 같은 값을 그대로 DB에 넣고 싶을 때가 있다. 정규화하기는 애매한 값이라든가, 특정 row에 딸린 id 목록이라든가, 통째로 읽고 쓰는 설정 덩어리 같은 것들이다. 관계로 풀어서 별도 테이블을 만들기는 번거롭고, 마침 MySQL에는 JSON 타입이 있다. 그래서 엔티티 필드를 Map로 두고 JSON 컬럼에 직렬화해서 저장한다. 편하다.그런데 문득 궁금해졌다. 이게 DB 안에서는 실제로 어떻게 처리될까? 성능상 괜찮을까? "JSON 쓰지 마라" 같은 말은 들어봤지만, 정작 왜 그런지는 깊이 따져본 적이 없었다.결론부터 말하면, JSON 컬럼은 데이터를 어떻게 접근하고 얼마나 큰지에 따라 충분히 쓸 만하기도, 독이 되기도 한다.그래서 직접 MySQL 8.0을 띄워놓고..

2026.06.21

개발 공부/DB [컬럼형 DB 파헤치기] 2편 - MySQL vs ClickHouse 성능 측정

시작하기 전에1편에서 컬럼형 DB가 왜 빠른지를 이론으로 정리했었다. 필요한 컬럼만 읽고, 같은 타입이 모여 있어 압축이 잘 되고, Zone Map으로 블록을 건너뛰고, 단건 쓰기는 약하지만 벌크는 강하다는 식의 이야기들이었다.이번 편에선 그게 실제로 그런지 직접 측정해봤다. MySQL과 ClickHouse를 같이 띄워서, 같은 데이터를 양쪽에 적재하고, 같은 쿼리를 던졌다. 결과는 1편 이론이 거의 그대로 입증된 부분이 많았고, 한두 가지는 미묘하게 어긋나기도 했다.전체 코드와 스크립트는 GitHub repo에 올려뒀다. 직접 재현해보고 싶으면 그대로 돌려보면 된다.시나리오 - 메트릭 모니터링비교 시나리오는 1편 마지막에 다뤘던 메트릭 모니터링 으로 정했다. 데이터 모양이 시계열이라 직관적이고, 1편에..

2026.05.31

개발 공부/DB [컬럼형 DB 파헤치기] 1편 - Row DB와 뭐가 다른가

"대규모 시스템 설계 면접" 책을 보다 보면 메트릭 모니터링이나 광고 클릭 집계 챕터에서 컬럼형 DB(컬럼 지향 데이터베이스)가 해법으로 툭툭 나온다. 근데 "왜 하필 여기서 컬럼형이지?"가 잘 와닿지 않았다. 평소에 MySQL만 쓰다 보니 더 그랬던 것 같다.그래서 한 번 제대로 정리해보기로 했다. 글이 길어질 것 같아서 두 편으로 나눴는데, 이번 편은 이론이다. 컬럼형 DB가 뭔지, MySQL 같은 row DB와 내부적으로 뭐가 다른지, 어떤 워크로드에서 빨라지는지를 정리한다. 다음 편에서는 DB를 직접 띄워서 같은 데이터로 MySQL과 성능을 비교해볼 생각이다.컬럼형 DB란 - 디스크에 데이터를 늘어놓는 방식의 차이컬럼형 DB를 한 줄로 설명하면 이렇다.같은 테이블 데이터를, 디스크에 컬럼 단위로 ..

2026.05.24

개발 공부/DB 왜 서로 다른 값을 INSERT 했는데 데드락이 걸릴까?

최근 서로 다른 트랜잭션에서 겹치지 않는 FK 값을 가진 엔티티들을 동시에 삽입할 때 데드락이 발생한 케이스가 있었다. 분명 범위가 겹치지 않는데 왜 데드락이 걸리는 걸까?1. 결론: 유니크/외래 키 제약조건이 데드락을 유발한다핵심 원인외래 키(Foreign Key)이거나 유니크 제약 조건(Unique Constraint)이 걸려 있다면, 데드락 발생 확률이 비약적으로 높아진다.데드락 발생 메커니즘상황: FK 에는 유니크 제약 조건이 걸려있고 기존 데이터는 10, 20 이 존재한다고 가정. 격리 수준은 REPEATABLE READ.트랜잭션 A (INSERT 12): 유니크 체크를 위해 10~20 사이에 공유 잠금(S-Lock, 갭 락)을 획득트랜잭션 B (INSERT 14): 마찬가지로 10~20 사이에..

2026.02.02

개발 공부/기타 [Case Study] SLASH 22 - 왜 은행은 무한스크롤이 안 되나요

일반 은행과 토스뱅크의 거래 내역 조회 방식 비교 및 시스템 아키텍처일반 은행 앱의 거래 내역 조회 방식의 불편함일반 은행 앱은 거래 내역 조회 시 상단에 기간 설정을 요구한다. 지정된 기간만큼만 스크롤하여 조회할 수 있으며, 그 이상을 조회하려면 유저가 직접 조회 기간을 변경해야 한다. 반면, 트위터, 인스타그램, 카카오톡 등 많은 인터넷 서비스들은 기간 설정을 요구하지 않고, 스크롤만 계속하면 십수 년 전의 글까지 나열되는 우아한 스크롤 UI를 제공한다. 일반 은행이 이러한 불편한 인터페이스를 제공하는 이유는 시스템 아키텍처 때문이다.은행 시스템의 일반적인 분리 구조 (채널계와 계정계)계정계 (Core Banking System)실제로 유저의 돈을 다루며 원본 데이터가 저장되는 영역이다. 장애나 오류..

2025.12.01

개발 공부/Spring 엄청 커다란 파일 다운로드

만약 서버 → 클라이언트로 큰 크기의 파일을 생성해서 반환해야 된다고 생각해보자. 예를 들어 전체 유저에 대한 통계 데이터를 생성 + 압축해서 클라이언트에게 반환해야 한다고 해보자. 만약 완성된 전체 데이터의 크기가 3GB 라고 하면 메모리가 3GB 이하인 (혹은 그 이상이어도) 서버는 버티지 못하고 OOM 으로 죽어버릴 것이다. 또한 통계성으로 유저에 대한 이런저런 정보를 모두 모아야 해서, 생성하는데 시간이 오래걸리는 데이터라면 timeout 도 발생할 가능성이 있다. 이러한 문제를 어떻게 해결하면 좋을까??1. 스트리밍 다운로드가장 먼저 생각난 해결책은 스트리밍으로 파일을 반환하는 방법이다. 일반적으로라면 아래의 과정을 따를텐데, 1. 전체 유저에 대한 데이터 생성2. `.zip` 으로 압축3. 클..

2025.11.23

개발 공부/Java 자바 Collector 구조 (feat. groupingBy() 사용법)

Collectors의 groupingBy() 메서드를 사용하면 아래와 같이 데이터 스트림을 맵으로 편리하게 반환할 수 있어서 프로젝트나 알고리즘 문제를 풀 때 종종 사용했었다. 그런데 쓸 때마다 groupingBy() 메서드의 API 문서를 보며 아 이렇게 쓰는 거였지 하고 그대로 따라 치곤 했다.public class GroupingByTest { @Getter static class Person { private final int age; private final String name; public Person(int age, String name) { this.age = age; this.name = name; ..

2024.01.14

개발 공부/Spring 테스트에서만 @Async 적용되지 않도록 하기

⭐️ 방법 매우 간단함 주의.. 문제 상황 알림 발송 로직을 비동기로 처리하고 테스트를 수행하려니 아래와 같이 알림 발송을 검증하는 테스트가 깨지는 문제가 생겼다. 원인은 비동기 메서드인 notificationService.send()가 완료되기 이전에 값이 생성되었는지를 검증하려고 했기 때문이다. 더 큰 문제로, 비단 알림 발송 자체를 검증하는 테스트 뿐만 아니라 다른 테스트에서도 해당 테스트가 수행하는 로직이 특정 작업을 마치고 알림 발송을 수행하는 경우 동일하게 비동기 로직이 수행되어 테스트가 깨지고 있었다. 그 예시로, 위의 경우 테스트 완료 후 수행하는 reset.sql 에서 알림 테이블을 지울 때 존재하지 않던 알림이 (비동기로 실행되어) 회원 테이블을 지울 때 존재하게 되는 상황이다. 때문에..

2023.12.27

개발 공부/Spring [플랭고] 주요/부가 로직 트랜잭션 분리하기 - TransactionalEventListener와 REQUIRES_NEW

플랭고는 친구와 함께 일정을 수정하고 장소를 추가하는 등 일정 공유 기능을 제공한다. 이때 새로운 일정에 초대되거나, 일정 초대를 수락/거절하거나 공유 중인 일정이 수정되는 등의 상황에 알림(자체 알림 목록 + 푸시)이 발송되도록 기능을 구현해두었다.기존에 알림을 발송하는 흐름은 다음과 같다. 먼저 일정 서비스에서 알림을 발송해야 하는 경우에 알림 DTO를 만들어 알림 서비스를 호출한다. 그러면 알림 서비스는 알림을 생성하고 외부 서비스인 FCM을 통해 대상 회원의 기기로 푸시 알림을 보낸다.이러한 흐름으로 알림을 발송하면 위 그림처럼 일정 -> 알림 -> 외부 서비스를 거치는 흐름 전체가 하나의 트랜잭션으로 묶이게 된다. 이런 식으로 코드를 구현하면 크게 다음과 같은 문제가 생길 수 있다고 생각했다. ..

2023.11.27