본문 바로가기

전체 글

(55)
[Nginx] Nginx 이벤트 루프와 epoll 기반 I/O 다중화 NGINX 한 대가 초당 수만 건의 요청을 처리하면서도 메모리를 수백 MB 이상 잡아먹지 않는다. 스레드 기반 서버라면 스택만으로 수 GB가 사라지는 상황에서도 NGINX는 그러지 않는다. 이것이 가능한 이유는 하나다. I/O를 기다리지 않기 때문이다. Apache가 연결마다 스레드를 할당하는 구조적 한계를 드러내던 자리에서, NGINX는 OS 커널의 이벤트 알림 메커니즘을 활용해 단 몇 개의 프로세스로 수만 개의 연결을 동시에 관리한다. 이 글에서는 그 원리를 이벤트 루프(Event Loop)와 I/O 다중화(epoll/kqueue)를 중심으로 OS 레벨까지 파헤쳐본다.문제의 시작 - C10K2000년대 초, 웹 트래픽이 폭발적으로 증가하면서 하나의 서버로 동시 접속자 1만 명(10,000 Connec..
[데이터베이스] 데이터베이스 복구 메커니즘 — Redo, Undo, Checkpoint 트랜잭션을 커밋했다. 그런데 그 직후 서버가 죽었다. 데이터는 살아있는가?이 질문에 "그렇다"고 답할 수 있으려면 데이터베이스가 내부적으로 어떤 메커니즘을 갖추고 있는지 이해해야 한다. ACID의 D, Durability(지속성)는 그냥 선언이 아니다. Redo Log, Undo Log, Checkpoint라는 구체적인 구현이 그것을 보장한다.ACID와 복구의 관계ACID에서 Undo, Redo와 직접 연결된 속성은 세 가지다. Atomicity(원자성):트랜잭션은 전부 반영되거나 전혀 반영되지 않아야 한다. 중간 상태로 남으면 안 된다. 트랜잭션 도중 실패했을 때 지금까지의 변경을 모두 되돌려야 하는데, 이것이 Undo Log의 첫 번째 역할이다.Isolation(고립성):트랜잭션이 진행 중인 동안 다..
[MySQL] 트랜잭션 격리가 구현되는 원리 — InnoDB MVCC 데이터베이스를 다루는 개발자라면 트랜잭션 격리 수준을 한 번쯤 공부한다. REPEATABLE READ에서는 Phantom Read가 발생할 수 있고, READ COMMITTED에서는 Non-repeatable Read가 발생한다. 이 정도는 많이 알고 있지만 "왜 그런가"를 설명할 수 있는 사람은 훨씬 적다. 격리 수준은 단순한 설정 플래그가 아니다. 그 아래에는 InnoDB가 데이터를 버전 단위로 관리하는 정교한 메커니즘이 있다. 이것이 MVCC(Multi-Version Concurrency Control)다. MVCC를 이해하면 격리 수준의 동작이 "그냥 그렇게 되는 것"이 아니라 필연적인 결과임을 알게 된다. 그리고 READ COMMITTED와 REPEATABLE READ를 언제 선택해야 하는지, ..
[MySQL] MySQL Primary-Replica: 내부 동작 원리부터 운영까지 이번 글에서는 복제가 내부적으로 어떻게 동작하는지에 대해 네트워크 프로토콜 수준부터 스레드 아키텍처, GTID 복구 메커니즘, 장애 시나리오별 동작, 그리고 실제 운영에서 놓치기 쉬운 함정들까지 깊게 다룬다.Replica 복제는 어떤 방식으로 어떻게 전송되는가1. MySQL 복제는 TCP 위의 이벤트 스트리밍많은 사람들이 복제를 "주기적으로 변경 내역을 가져오는 것"으로 막연하게 이해한다.실제로는 TCP 위에서 동작하는 MySQL 고유의 Binary Protocol로 이벤트를 스트리밍하는 구조다.OSI 계층 관점:Layer 7 (Application) → MySQL Binary Protocol (COM_BINLOG_DUMP_GTID)Layer 6 (Presentation) → 선택적 압축 (zstd /..
[Kubernetes] Kubernetes 아키텍처 - kubectl 명령 이후의 동작 이 글은 Kubernetes를 처음 접하는 사람들이 "도대체 내가 입력한 명령이 어디로 가는 거지?" 라는 궁금증을 해소할 수 있도록, 아키텍처 전체를 순서대로 뜯어보는 글이다.Kubernetes란 무엇인가?컨테이너를 혼자 쓰면 어떤 문제가 생길까?Docker를 처음 배우면 docker run 명령 하나로 컨테이너를 실행하는 것이 마법처럼 느껴진다. 하지만 실제 서비스를 운영하다 보면 금방 한계에 부딪힌다.컨테이너가 죽으면? → 직접 다시 실행해야 한다트래픽이 갑자기 몰리면? → 수동으로 컨테이너를 늘려야 한다서버가 10대, 100대면? → 어느 서버에서 무엇이 실행되는지 파악하기 어렵다배포 중 서비스를 중단하지 않으려면? → 복잡한 스크립트를 직접 작성해야 한다이 문제들을 해결하기 위해 등장한 것이 컨..
[MySQL] Container MySQL Primary-Replica 셋팅 읽기와 쓰기가 같은 DB 인스턴스를 공유하면 검색 쿼리가 커넥션과 CPU를 독점하여 쓰기 트랜잭션이 타임아웃된다.이 문서는 기존 데이터를 유지하면서 단일 MySQL 컨테이너를 Primary(쓰기 전용) - Replica(읽기 전용) 구조로 전환하는 전 과정을 다룬다.아키텍처 개요[Application] │ ├── Write (INSERT/UPDATE/DELETE) → [Primary :3306] 쓰기 전용 └── Read (SELECT) → [Replica :3307] 읽기 전용[Primary] Binlog(ROW) ──── 비동기 복제 ────→ [Replica] max_connections=60 max_connection..
[Redis] Cache Stampede: 캐시가 만료되는 순간, 모든 요청이 DB로 몰린다면 Cache Stampede란?Cache Stampede(Thundering Herd)는 동일한 캐시 키가 만료되는 시점에 다수의 요청이 동시에 DB로 몰리는 현상이다.정상적인 캐시 흐름에서는 Cache Miss가 발생하면 하나의 요청이 DB에서 데이터를 가져와 캐시에 저장하고, 이후 요청은 캐시에서 처리된다. 하지만 캐시가 만료되는 바로 그 순간에 여러 요청이 동시에 도착하면 상황이 달라진다.모든 요청이 캐시 미스를 경험하고, 각각 독립적으로 DB에 동일한 쿼리를 실행한다. 요청이 수백, 수천 건이라면 DB에 순간적인 부하 폭증이 발생한다.왜 발생하는가?Write Through로 해결할 수 없는가?"캐시를 항상 최신 상태로 유지하면 만료될 일이 없지 않은가?"라는 생각이 들 수 있다. Write Thro..
[Redis] Cache Penetration: 존재하지 않는 데이터에 대한 요청이 캐시를 뚫고 DB를 때린다면 Cache Penetration이란? Cache Penetration은 존재하지 않는 데이터에 대한 요청이 캐시를 무력화시키는 현상이다.일반적인 캐시 조회 흐름을 먼저 살펴보자.이 흐름은 데이터가 존재하는 경우에는 잘 동작한다. Cache Miss가 발생해도 DB에서 조회한 결과를 캐시에 저장하므로, 이후 동일한 요청은 캐시에서 처리된다.그런데 존재하지 않는 데이터를 요청하면 어떻게 될까?DB 조회 결과가 null이면 캐시에 저장할 값이 없다. 따라서 같은 요청이 반복될 때마다 매번 캐시를 통과해서 DB에 직접 도달한다. 이것이 Cache Penetration이다.왜 위험한가?단순히 한두 건의 요청이라면 큰 문제가 아니다. 하지만 다음과 같은 상황을 생각해보자.악의적인 사용자가 존재하지 않는 ID를 랜덤..