반응형

Database/MySQL 8

8편. MySQL 성능 문제는 쿼리가 아니라 구조일 때가 많다

성능 이슈가 발생하면 가장 먼저 떠오르는 접근은 보통 이렇다.“느린 쿼리가 있다 → 쿼리를 고치자.”물론 쿼리가 원인인 경우도 있다. 하지만 InnoDB 환경에서 반복적으로 관측되는 성능 문제의 상당수는, SQL 문법이나 실행 계획 이전에 메모리와 IO 구조에서 발생한다.InnoDB에서 쿼리는 어디를 지나갈까InnoDB에서 쿼리가 실행될 때, 실제로 가장 빈번하게 오가는 경로는 다음과 같다.인덱스 페이지 접근데이터 페이지 접근Buffer Pool 히트 또는 미스필요 시 디스크 IO즉, 쿼리 성능은 SQL 문장 자체보다 페이지가 메모리에 있는지, 디스크에 있는지에 훨씬 민감하다.Buffer Pool은 InnoDB 성능의 중심이다InnoDB의 Buffer Pool은 디스크에 있는 데이터와 인덱스 페이지를 메..

Database/MySQL 2026.02.01

7편. MySQL은 장애 상황에서 무엇을 보장할까

6편에서 트랜잭션 격리 수준이 동시성과 성능에 어떤 비용을 요구하는지 살펴봤다.이제 더 근본적인 질문으로 내려가 보자.“트랜잭션을 커밋했다고 했을 때, 정말로 무엇이 보장될까?”장애가 발생했을 때, MySQL(InnoDB)이 보장하는 것은 생각보다 명확하고, 동시에 제한적이다.커밋은 ‘데이터 파일에 쓰였다’는 의미가 아니다많은 개발자가 커밋을 이렇게 이해한다.“COMMIT = 데이터가 디스크에 안전하게 저장됨”InnoDB에서 이 이해는 정확하지 않다.커밋의 의미는 다음 한 문장으로 요약된다.“이 변경 사항은 복구 가능하다.”즉, 커밋 시점에 실제 데이터 페이지가 디스크에 기록되었는지는 중요하지 않다.Redo Log가 하는 일InnoDB는 데이터 페이지를 즉시 디스크에 쓰지 않는다.대신, 변경 내용을 Re..

Database/MySQL 2026.02.01

6편. 트랜잭션 격리 수준은 왜 공짜가 아닐까

5편에서 실행 계획과 옵티마이저가 “정답을 아는 존재가 아니다”라는 이야기를 했다.이번에는 그보다 더 근본적인 질문으로 내려가 보자.“트랜잭션 격리 수준을 올리면, 왜 성능이 나빠질까?”많은 경우 격리 수준은 단순한 설정 값처럼 취급된다. 하지만 InnoDB에서 격리 수준은 동시성-성능-정합성 사이의 명시적인 트레이드오프다.격리 수준이 존재하는 이유트랜잭션 격리 수준은 “얼마나 서로를 신경 쓰지 않고 동작할 수 있는가”를 정의하는 규칙이다.동시에, 다음 질문에 대한 답이기도 하다.“다른 트랜잭션의 변경을 어디까지 허용할 것인가?”이 선택이 곧 성능과 동시성의 한계를 결정한다.InnoDB가 제공하는 격리 수준InnoDB는 표준 SQL 격리 수준을 모두 지원한다.READ UNCOMMITTEDREAD COMM..

Database/MySQL 2026.02.01

5편. 실행 계획을 믿으면 안 되는 이유

4편에서 인덱스의 내부 구조를 기준으로 왜 읽기는 빠르고 쓰기는 느릴 수밖에 없는지 살펴봤다.이제 실무에서 가장 자주 나오는 질문으로 넘어가 보자.“인덱스도 있는데, 왜 실행 계획이 풀스캔을 선택했지?”이 상황에서 흔히 이런 결론에 도달한다.“옵티마이저가 잘못 판단했다.”반은 맞고, 반은 틀리다. 실행 계획을 이해하려면, 먼저 옵티마이저가 무엇을 할 수 있고, 무엇을 할 수 없는지를 정확히 알아야 한다.MySQL 옵티마이저의 역할MySQL 옵티마이저는 SQL을 실행하기 전에 가능한 여러 실행 방법을 비교한다.그리고 그중에서 비용(cost)이 가장 낮다고 추정되는 계획을 선택한다.중요한 점은, 이 비용이 실제 비용이 아니라 추정 비용라는 것이다.옵티마이저는 무엇을 보고 판단할까옵티마이저는 다음 정보를 기반..

Database/MySQL 2026.02.01

4편. 인덱스는 왜 읽기는 빠르고 쓰기는 느릴까

3편에서 락이 왜 걸리고, 왜 생각보다 넓은 범위를 잠그는지 살펴봤다.이 지점에서 또 하나의 질문이 자연스럽게 이어진다.“인덱스를 추가했더니 조회는 빨라졌는데, 쓰기 성능은 왜 더 나빠졌을까?”이 현상은 우연이 아니다. 인덱스의 내부 구조를 이해하면 읽기와 쓰기의 성능 차이가 필연이라는 점이 보인다.인덱스는 단순한 보조 테이블이 아니다인덱스를 흔히 “검색을 빠르게 해주는 자료구조”라고 설명한다.하지만 InnoDB에서 인덱스는 단순한 보조 수단이 아니다. 데이터 접근 방식 자체를 결정하는 구조다.InnoDB의 모든 인덱스는 기본적으로 B-Tree 구조를 사용한다.B-Tree가 읽기에 강한 이유B-Tree는 다음 조건에 최적화된 자료구조다.정렬된 상태 유지범위 조회에 유리트리 높이를 낮게 유지이 구조 덕분에..

Database/MySQL 2026.02.01

3편. 락은 왜 걸렸고, 왜 풀리지 않았을까

2편에서 MVCC가 읽기와 쓰기의 충돌을 어떻게 줄이는지 살펴봤다.하지만 실무에서는 이런 상황을 훨씬 자주 마주친다.“읽기 쿼리는 잘 되는데, UPDATE가 갑자기 멈췄다.” “트랜잭션이 끝났는데도 락 대기가 풀리지 않는다.”이 문제의 원인은 대부분 MVCC가 해결하지 못한 영역, 즉 락(Lock)에 있다.InnoDB에서 락은 왜 필요한가MVCC가 있어도 모든 상황을 과거 버전 읽기로 해결할 수는 없다.특히 다음 상황에서는 동시 쓰기 충돌을 반드시 막아야 한다.같은 행을 동시에 UPDATE존재 여부를 전제로 한 INSERT범위 조건을 만족하는 행 수정이때 InnoDB는 과거 버전을 보여주는 대신, 락으로 질서를 강제한다.Row Lock - 가장 기본적인 락가장 직관적인 락은 Row Lock이다.특정 행 ..

Database/MySQL 2026.02.01

2편. MVCC는 무엇을 해결하고, 무엇을 해결하지 못할까

1편에서 트랜잭션이 왜 본질적으로 느릴 수밖에 없는지, Undo Log와 Redo Log의 비용 관점에서 살펴봤다.이쯤에서 자연스럽게 이런 의문이 든다.“그런데 읽기 트랜잭션은 왜 락 없이도 동작할 수 있을까?”이 질문에 대한 답이 바로 MVCC(Multi-Version Concurrency Control)다.MVCC가 등장한 이유전통적인 락 기반 동시성 제어에서는 읽기와 쓰기가 서로를 쉽게 막는다.읽는 동안 쓰기가 대기한다쓰는 동안 읽기가 대기한다동시 사용자가 늘어날수록, 처리량은 급격히 떨어진다.InnoDB는 이 문제를 해결하기 위해 “락을 덜 거는 방법”이 아니라 과거 버전을 읽게 하는 방법을 선택했다.그 결과물이 MVCC다.MVCC의 핵심 아이디어MVCC의 핵심은 단순하다.“읽을 때 현재 값을 보..

Database/MySQL 2026.02.01

1편. 트랜잭션은 왜 생각보다 느릴까

백엔드 개발을 하다 보면 이런 순간을 만나게 된다.“로직은 단순한데, 트랜잭션을 걸자마자 성능이 확 떨어졌다.”이때 보통 다음부터 의심한다.쿼리가 느린가?인덱스가 부족한가?락이 걸렸나?하지만 이 질문들은 모두 결과를 보고 추측하는 단계다. 트랜잭션이 느린 이유는 그보다 훨씬 앞단에 있다.이 글에서는 MySQL(InnoDB) 공식 문서를 기준으로, 트랜잭션이 왜 본질적으로 비용이 드는 작업인지를 내부 구조 관점에서 정리한다.트랜잭션을 시작한다는 것은 무슨 의미일까애플리케이션 코드에서 트랜잭션은 보통 이렇게 보인다.@Transactional 하나, 또는 BEGIN / COMMIT.하지만 InnoDB 입장에서 트랜잭션을 시작한다는 것은 단순한 모드 전환이 아니다.내부적으로 다음 준비 작업이 동시에 시작된다.일..

Database/MySQL 2026.02.01
반응형