Post

2025-08-13-TIL

Today I Learned

2025-08-13-Mentoring

오늘은 간만에 가슴에 불을 지피는 멘토링이었다. 최근에 개인적인 이유로 멘토링에 대한 학습시간을 많이 내지 못 했는데, 열심히 해야겠다는 다짐이 다시 생겼다.

MySQL의 레코드 락

InnoDB 스토리지 엔진 수준의 잠금 기능으로, 레코드 자체만을 잠그는 락을 의미한다. 실제로는 인덱스의 레코드를 잠근다. 인덱스가 없더라도 내부적으로 자동 생성된 인덱스로 잠근다.

락 종류설명사용 상황
공유 락 (S-Lock)여러 트랜잭션이 같은 행을 읽을 수 있지만 쓰기는 불가SELECT ... LOCK IN SHARE MODE
배타 락 (X-Lock)해당 행을 읽고 쓸 수 있으며, 다른 트랜잭션 접근 차단SELECT ... FOR UPDATE
의도 공유 락 (IS)트랜잭션이 향후 공유 락을 걸 의도가 있음행 단위 공유 락 전에 테이블에 설정
의도 배타 락 (IX)트랜잭션이 향후 배타 락을 걸 의도가 있음행 단위 배타 락 전에 테이블에 설정
Gap Lock인덱스 사이의 “간격”을 잠금, 새로운 행 삽입 방지REPEATABLE READ에서 넥스트 키 락의 일부
Next-Key Lock행 락 + Gap Lock, 범위 내 삽입/변경 방지RR 격리 수준에서 팬텀 리드 방지
Record Lock단일 인덱스 레코드에 대한 잠금정확한 키 조회 시 사용

트랜잭션이란?

트랜잭션(Transaction)이란 데이터베이스에서 하나의 논리적인 작업 단위를 의미합니다. 여러 개의 작업(예: SQL 문장들)을 하나로 묶어서 모두 성공하거나 모두 실패하도록 처리하는 것이 핵심입니다.


ACID란?

데이터베이스에서 ACID는 트랜잭션이 안전하고 일관되게 처리되도록 보장하는 4가지 핵심 속성을 뜻한다.

참고로, ACID와 반대 개념인 BASE는 Basically Available, Soft state, Eventual consistency을 의미한다. ACID 데이터베이스는 가용성 보다는 즉각적인 일관성을 우선시한다.

MongoDB에서 ACID 트랜잭션을 지원한다!!! 4.2버전에서!!! -> RDB에서만 지원하는건 아니다!!

Atomicity (원자성)

원자성은 트랜잭션이 전부 수행되거나, 전혀 수행되지 않아야 함을 의미한다. 중간에 실패하면 모든 변경 사항이 취소(rollback)되어 DB는 트랜잭션 전 상태로 되돌아간다. 예를 들어, 은행 계좌 이체에서 돈이 빠져나가기만 하고 들어가지 않는 상황이 없어야 한다.

원자성을 보장하기 위해서는 전부 수행되거나, 전혀 수행되지 않도록 진행상황과 상태를 어딘가에 기록해야한다. 그리고 중간에 실패한다면 트랜잭션 안에서 수행한 여러가지 연산이나 쿼리가 최종적으로 DB에 반영되지 않도록 보장해야한다.

InnoDB는 Undo Log에 “변경 전 값/반대 연산”을 남겨두고 작업을 진행한다. 실패 시 Undo Log로 완전히 되돌릴 수 있어 트랜잭션이 “All or Nothing”의 성질을 가진다. 커밋 후에도 Undo 레코드는 MVCC를 위해 잠시 남아 있다가 Purge로 정리된다.

-> 여러개의 접근에서 원자성을 지키기위한 설명으로 이어지는게 더 매끄럽다!

Consistency (일관성)

트랜잭션이 완료되면 데이터베이스의 무결성과 규칙이 항상 유지되어야 한다. 다시 말해, DB의 스키마, 제약 조건(Constraints), 비즈니스 규칙 등이 위배되지 않아야 한다. 예를 들어, 쇼핑몰 DB의 상품 테이블에서 상품 재고가 음수가 되면 안 된다.

일관성을 보장하기 위해서는 규칙의 위반에 대한 처리가 필요하다. 외래키, 유니크 인덱스, 체크 제약, 트리거 등 무결성 규칙을 통해 커밋 전후 데이터의 논리적 일관성을 보장한다. 제약 위반 시 즉시 롤백되며, 커밋된 상태는 항상 규칙을 만족하도록 유지된다. 이러한 처리를 통해 최종적으로는 유효한 데이터의 일관성을 보장할 수 있다.

-> 분산 시스템에서도 이 속성을 지원하기 위해서 여러가지 기술들이 존재한다. 2PC (Two Phase Commit). SAGA 패턴 (보상 트랜잭션), Eventual Consistency + Conflict Resolution(일단 저장, 나중에 충돌 해결)

  • 일단 데이터를 저장하고, 나중에 충돌 해결
  • NoSQL 시스템(Cassandra, DynamoDB 등)의 일반적인 방식

Isolation (고립성)

동시에 여러 트랜잭션이 수행될 때, 서로 간섭하지 않고 독립적으로 실행된 것처럼 보여야 한다. 즉, 실제로는 동시에 실행되더라도, 결과는 마치 순차적으로 실행된 것과 동일해야 한다. 예를 들어, 두 사람이 동시에 같은 좌석을 예매하려 할 때, 한 명만 성공해야 한다.

동시에 실행되더라도 순차적으로 실행된 것처럼 보이게 하려면, 결국에는 시간순으로 줄을 세우는 방법뿐이다. 이 순차적인 직렬화를 구현하기 위해서는 여러가지 방법이 있겠지만, 가장 간단한 방법은 경합에 대한 락(Lock)을 구현해서 줄이 세워지도록 하는 것이다.

MySQL InnoDB에서는 MVCC(Undo 기반) 로 읽기 스냅샷을 제공하여 다른 트랜잭션 변경의 간섭을 줄인다. 갱신 경합에는 Row Lock, 팬텀 방지에는 Gap/Next‑Key Lock을 사용한다. 기본 격리 수준(REPEATABLE READ)에서 동일 쿼리는 트랜잭션 동안 일관된 결과를 본다. 필요 시 READ COMMITTED/READ UNCOMMITTED/SERIALIZABLE로 조정 가능하다.


D — Durability (지속성, 내구성)

트랜잭션이 성공적으로 완료되면, 그 결과는 전원 꺼짐, 장애 등에도 영구적으로 저장되어야 한다. DBMS는 이를 위해 로그, 백업, 디스크 저장 등의 메커니즘을 사용한다. 예를 들어, 주문이 완료된 뒤 서버가 꺼져도 주문 기록은 사라지지 않아야 한다.

InnoDB에서는 다음과 같은 기술을 구현하여 지속성을 보장한다. 먼저, WAL(Write‑Ahead Logging)는 데이터 파일에 쓰기 전에 Redo Log에 변경을 먼저 기록한다. 커밋 시 fsync로 로그를 디스크에 내리며, 장애가 나도 Redo Log 재적용으로 복구한다. 그리고 Doublewrite Buffer를 통해 일부 페이지만 쓰여 발생하는 Partial Page Write를 방지한다.

-> ANSI에서 ACID는 다르다! 이 내용을 확인해보자! DB마다 어떤 차이가 있는지도! -> write ahead log와 redo log의 구현에 대해서 잘 공부하면 실제 장애극복 시나리오에서도 잘 적용할 수 있다!!! -> Undo log 구현 이해안되는 부분 생각해보기


BEGIN TRANSACTION하고 COMMIT말고 뭐가 있나?

BEGIN TRANSACTIONCOMMIT은 데이터베이스 트랜잭션을 시작하고 완료하는 데 사용하는 명령어들이며, 이들과 함께 자주 사용되는 명령어들이 있습니다. 아래는 관련된 주요 트랜잭션 제어 명령어들입니다:

BEGIN / BEGIN TRANSACTION: 트랜잭션을 시작함. COMMIT: 트랜잭션 내의 모든 작업을 영구적으로 저장. ROLLBACK: 트랜잭션 내의 모든 변경 사항을 취소. SAVEPOINT: 트랜잭션 내에 중간 저장 지점을 설정. 이후 이 지점까지 ROLLBACK 할 수 있음 (부분 롤백 가능). ROLLBACK TO SAVEPOINT: 특정 저장 지점까지 롤백 (부분 롤백). 트랜잭션은 계속 유지됨. RELEASE SAVEPOINT: 더 이상 필요 없는 SAVEPOINT 제거. 대부분 명시적으로 해제하지 않아도 COMMIT 시 자동으로 제거됨.


REDO, UNDO Log

UNDO 로그는 트랜잭션 격리 수준을 보장하기 위한 로그이다. InnoDB에서는 DML로 변경되기 이전 버전의 데이터를 별도로 백업하며, 이 백업된 데이터를 Undo Log라고 한다.

  • 트랜잭션 보장: 트랜잭션이 롤백되면 Undo Log에 백업해둔 이전 버전의 데이터를 이용해 복구한다.
  • 격리 수준 보장: 그리고 특정 커넥션에서 데이터 변경 도중에 다른 커넥션에서 데이터를 조회하면 트랜잭션 격리 수준에 맞게 변경중인 레코드를 읽지 않고 Undo Log에 백업해둔 데이터를 읽어서 반환한다.

Undo Log가 누적되면 빈번하게 변경된 레코드를 조회하는 쿼리의 성능이 느려지는 문제가 있다. 이전에는 디스크 용량과 백업문제가 있었지만, MySQL 5.7, 8.0에서는 돌아가면서 순차적인 사용으로 디스크 공간을 줄이고, 자동으로 사용 공간을 줄여주기도 하여 해결되었다.

REDO 로그는 서버가 비정상적으로 종료됐을 때 데이터 파일에 기록되지 못한 데이터를 잃지 않게 해주는 안전장치이다. 트랜잭션의 ACID 중에서 Durable과 밀접한 연관이 있다. DB 서버가 비정상 종료되는 경우 두 가지 일관되지 않은 데이터를 가질 수 있다.

  • 커밋됐지만 데이터 파일에 기록되지 않은 데이터 -> Redo Log에 저장된 데이터를 데이터 파일에 복사하면 된다.
  • 롤백됐지만 데이터 파일에 이미 기록된 데이터 -> 변경되기 전 데이터(Undo Log)를 데이터 파일에 복사하면 된다. 하지만 상태파악을 위해서 Redo Log도 필요하다.

  • 윈도우란?

    핵심 개념은 “일정한 범위(창, window)를 기준으로 데이터를 다룬다”는 것이다. 알고리즘에서 슬라이딩 윈도우란, 배열이나 리스트에서 고정 크기 윈도우를 이동시키며 값을 처리하는 것이다.

    흐름 제어/속도 제한 (Rate Limiting 등)에서는

    • 의미: 클라이언트의 요청 수를 일정한 시간창(window) 동안 제한.
    • 예시: Fixed Window: 매 1분 동안 최대 100요청 허용 Sliding Log / Sliding Window: 최근 60초간 요청 수 기반으로 판단
    • 용도: API 서버 보호, DDoS 방지, QoS 확보

    -> 윈도우 방식 4가지!! 토큰 방식, 버킷 방식. 튀는 트래픽을 대응할 때 무슨 윈도우를 사용할거냐 등등

  • 분산락이란?

    분산락(Distributed Lock)은 분산 시스템에서 여러 프로세스나 노드가 공유 자원에 동시에 접근하지 못하도록 제어하기 위해 사용하는 동기화 메커니즘입니다. 즉, 여러 서버나 인스턴스가 동시에 같은 데이터를 수정하려고 할 때 하나만 작업을 수행하도록 잠금을 거는 방식입니다. 분산락의 조건은 다음과 같다.

    조건설명
    상호 배제(Mutual Exclusion)한 번에 하나만 자원을 소유 가능
    데드락 방지락이 영원히 잡히지 않게 타임아웃이나 리트라이 설계 필요
    정합성분산 환경에서 노드 충돌 시에도 일관성 유지
    고가용성락 서버 장애 시에도 시스템 전체가 중단되지 않도록

    Redis 기반 분산락

    • SET key value NX PX 3000 (NX: 없을 때만, PX: 유효시간)
    • Redisson 등 라이브러리 사용
    • 대표적인 알고리즘: Redlock (by Redis creator)

    -> N개의 노드가 하나의 서버에 붙는다. -> SPOF -> 센티넬 모드에서 리더를 선출하는 방식은? 리더 선출에서 dual brain이 발생할 수 있다. backsecc 알고리즘, 카프카랑 연결할 수 있다. Zookeeper도 연관지어짐. kafka도 zookeeper없이 리더선출가능하다. 릴리즈 노트정도는 읽어보고 있구나 할 수 있다. -> 클러스터 모드란? -> 세마포어란? count down latch

  • DB 복제 방식 세 가지

    1. 비동기 복제 (Asynchronous Replication)

      기본적인 MySQL 복제 방식 Primary(Source)가 트랜잭션 커밋 후에 binlog를 Replica로 전송 Replica가 binlog를 읽고 실행하여 데이터 복제 ❗Primary가 실패할 경우, Replica가 최신 데이터를 갖지 않을 수 있음

    2. 반동기 복제 (Semi-synchronous Replication)

      Primary가 트랜잭션을 커밋하기 전에 적어도 1개의 Replica가 binlog를 수신했는지 확인 단, binlog를 받은 것만 확인하고 실제로 실행 완료까지는 기다리지 않음 ❗️일정 수준의 데이터 일관성내결함성을 보장

    3. 완전 동기 복제 (Group Replication / Galera Cluster)

      모든 노드가 동시에 트랜잭션을 적용 트랜잭션은 모든 노드의 동의를 받은 후 커밋됨 높은 데이터 정합성, 자동 장애 복구, 분산 환경에 적합 사용 기술: Group Replication (MySQL 공식), Galera Cluster (Percona, MariaDB)

    -> Quorum 정족수, Write Read Node개수를 가지고 만들어낸 공식 -> binlog, statement

  • 프록시 패턴, 싱글톤 패턴 구현

    Subject (주제): RealSubject와 Proxy가 구현하는 공통 인터페이스

    RealSubject (실제 객체): 실제 핵심 로직을 수행하는 객체

    Proxy (프록시 객체): RealSubject에 접근하기 전에 중간에서 제어를 수행

-> forward proxy vs reverse proxy -> nginx, kubernetes -> L4 proxy, L7 poxy? NLB, ALB? -> 두괄식으로 말하려면? 질문을 답할때 두괄식으로 설명해보라. -> 3-way-handshake, 4-way-handshake

  • 격리 수준

    격리 수준Dirty ReadNon-Repeatable ReadPhantom Read설명
    READ UNCOMMITTED발생함발생함발생함커밋 전 데이터도 읽음 (가장 빠르지만 안정성 낮음)
    READ COMMITTED방지발생함발생함오직 커밋된 데이터만 읽음 (Oracle 기본값)
    REPEATABLE READ (MySQL 기본값)방지방지발생함(InnoDB에서는 방지됨)같은 트랜잭션 내에서 같은 SELECT 결과를 보장
    SERIALIZABLE방지방지방지완전 직렬화, 동시성 매우 낮음

    InnoDB에서는 MVCC(Multi-Version Concurrency Control) 때문에 REPEATABLE READ에서도 Phantom Read가 사실상 발생하지 않음 -> 하지만 MVCC는 읽기 일관성만 제공할 뿐, 삽입을 막지 않기 때문에 팬텀을 물리적으로 차단하진 않는다. 팬텀 리드를 완전히 방지하려면 MVCC 외에 잠금(lock) 이 필요하다.

    하지만 SERIALIZABLE로 올리면 SELECT도 내부적으로 LOCK을 걸어서 동시성이 크게 떨어짐

    READ UNCOMMITTED → 빠르지만 Dirty Read 발생

    READ COMMITTED → Dirty Read 방지, 하지만 같은 SELECT 값이 달라질 수 있음

    REPEATABLE READ → 같은 트랜잭션 내에서는 같은 SELECT 결과 보장 (MySQL 기본값)

    SERIALIZABLE → 완전한 일관성, 하지만 성능 저하

-> PostgreSQL: SSI. Serializable이지만 성능에서 충분히 보장된다.

  • MVCC

    MVCC(Multi-Version Concurrency Control)는 여러 트랜잭션이 동시에 데이터를 읽고 쓸 수 있도록 하면서도 일관성을 보장하는 기술이다. -> 데이터를 수정할 때 덮어쓰지 않고, 버전을 새로 만들어 저장한다. 읽는 트랜잭션은 자신에게 맞는 버전을 읽는다.

  • 옵티마이저, ..


Redis 실제 질문하려고 했던 기대답변이??? ANSI에서 말하는 데드락 방지 표준 기술이다. MySQL에서도 마찬가지일텐데, 어떤 트랜잭션을? Live Lock

어떻게 레디스로 분산락을 구현하지? -> 레디스 싱글스레드 -> 센티넬에서 … IoC, DI중점적으로 질문!!

This post is licensed under CC BY 4.0 by the author.