CS 간단 정리 - 백엔드

데이터베이스 시스템에서 동시성을 제어하는 방법에 대해 설명해주세요.

대표적인 동시성 제어 방식으로 MVCC(Multi-Version Concurrency Control)Lock-Based Concurrency Control이 있습니다.
  • MVCC(Multi-Version Concurrency Control)
MVCC는 데이터의 여러 버전을 유지하여 트랜잭션이 동시에 데이터를 읽고 쓸 수 있도록 하는 방식입니다. 각 트랜잭션은 자신만의 일관된 스냅샷을 기반으로 데이터를 읽어, 다른 트랜잭션의 변경 사항에 영향을 받지 않습니다.
데이터의 각 버전을 유지하여 읽기 작업이 쓰기 작업과 독립적으로 이루어질 수 있습니다. 트랜잭션은 시작 시점의 스냅샷을 기반으로 데이터를 읽어, 다른 트랜잭션의 변경 사항을 보지 못합니다.
또한 읽기 작업 시 잠금을 사용하지 않아 높은 동시성을 제공합니다. 읽기 작업이 잠금에 의해 지연되지 않아, 읽기 중심의 애플리케이션에서 우수한 성능을 보입니다. 읽기 작업 시 잠금을 사용하지 않으므로, 쓰기 작업과의 충돌이 줄어듭니다. 하지만 여러 버전의 데이터를 유지해야 하므로 저장 공간이 더 많이 필요할 수 있습니다.
트랜잭션이 시작된 시점의 데이터 상태를 기반으로 읽기 작업을 수행하여 일관성을 유지합니다. 또 갭락과 넥스트키 락을 통해 팬텀 리드를 방지합니다.
  • Lock-Based Concurrency Control
Lock-Based 방식은 데이터에 접근할 때 잠금(Lock) 을 사용하여 동시성을 제어합니다. 트랜잭션이 데이터를 읽거나 수정할 때 해당 데이터에 잠금을 걸어 다른 트랜잭션의 접근을 제한합니다. 즉, 잠금을 통해 데이터의 일관성과 무결성을 직접적으로 제어합니다.
데이터에 접근할 때 잠금을 걸어 다른 트랜잭션의 접근을 제한합니다. 읽기 작업은 공유 잠금을, 쓰기 작업은 배타 잠금을 사용하여 동시성을 제어합니다. 많은 다수의 트랜잭션이 동일한 데이터에 접근할 경우 성능 저하가 발생할 수 있습니다. 또 잘못된 잠금 순서나 설계로 인해 교착 상태(Deadlock)가 발생할 위험이 있습니다.
  • MVCC와 Lock-Based Concurrency Control 둘 중 어떤 걸 사용해야 하나요? 🤔
실제 데이터베이스 시스템, 특히 MySQL의 InnoDB는 MVCC와 Lock-Based 방식의 장점을 결합하여 동시성 제어를 최적화합니다.
읽기 트랜잭션은 MVCC를 사용하여 일관된 스냅샷을 기반으로 데이터를 읽으므로, 잠금을 최소화하고 높은 동시성을 유지할 수 있습니다.
쓰기 트랜잭션은 잠금을 사용하여 데이터의 일관성과 무결성을 유지하면서, 동시에 데이터 충돌을 방지합니다.
매일메일 - 데이터베이스 시스템에서 동시성을 제어하는 방법에 대해 설명해주세요.
대표적인 동시성 제어 방식으로 **MVCC(Multi-Version Concurrency Control)** 와 **Lock-Based Concurrency Control**이 있습니다. ## MVCC(Multi-Version Concurrency Control) MVCC는 데이터의 여러 버전을 유지하여 트랜잭션이 동시에 데이터를 읽고 쓸 수 있도록 하는 방식입니다. 각 트랜잭션은 자신만의 **일관된 스냅샷**을 기반으로 데이터를 읽어, 다른 트랜잭션의 변경 사항에 영향을 받지 않습니다. 데이터의 각 버전을 유지하여 읽기 작업이 쓰기 작업과 독립적으로 이루어질 수 있습니다. 트랜잭션은 시작 시점의 스냅샷을 기반으로 데이터를 읽어, 다른 트랜잭션의 변경 사항을 보지 못합니다. 또한 **읽기 작업 시 잠금을 사용하지 않아 높은 동시성**을 제공합니다. 읽기 작업이 잠금에 의해 지연되지 않아, 읽기 중심의 애플리케이션에서 우수한 성능을 보입니다. 읽기 작업 시 잠금을 사용하지 않으므로, 쓰기 작업과의 충돌이 줄어듭니다. 하지만 여러 버전의 데이터를 유지해야 하므로 저장 공간이 더 많이 필요할 수 있습니다. 트랜잭션이 시작된 시점의 데이터 상태를 기반으로 읽기 작업을 수행하여 일관성을 유지합니다. 또 갭락과 넥스트키 락을 통해 팬텀 리드를 방지합니다. ## Lock-Based Concurrency Control Lock-Based 방식은 데이터에 접근할 때 **잠금(Lock)** 을 사용하여 동시성을 제어합니다. 트랜잭션이 데이터를 읽거나 수정할 때 해당 데이터에 잠금을 걸어 다른 트랜잭션의 접근을 제한합니다. 즉, 잠금을 통해 데이터의 일관성과 무결성을 직접적으로 제어합니다. 데이터에 접근할 때 잠금을 걸어 다른 트랜잭션의 접근을 제한합니다. **읽기 작업은 공유 잠금**을, **쓰기 작업은 배타 잠금**을 사용하여 동시성을 제어합니다. 많은 다수의 트랜잭션이 동일한 데이터에 접근할 경우 성능 저하가 발생할 수 있습니다. 또 잘못된 잠금 순서나 설계로 인해 교착 상태(Deadlock)가 발생할 위험이 있습니다. ## MVCC와 Lock-Based Concurrency Control 둘 중 어떤 걸 사용해야 하나요? 🤔 실제 데이터베이스 시스템, 특히 MySQL의 InnoDB는 MVCC와 Lock-Based 방식의 장점을 결합하여 동시성 제어를 최적화합니다. **읽기 트랜잭션은 MVCC를 사용**하여 일관된 스냅샷을 기반으로 데이터를 읽으므로, **잠금을 최소화**하고 **높은 동시성**을 유지할 수 있습니다. **쓰기 트랜잭션은 잠금을 사용**하여 **데이터의 일관성과 무결성을 유지**하면서, 동시에 **데이터 충돌을 방지**합니다. ## 추가 학습 자료를 공유합니다. - [[Database] MVCC(다중 버전 동시성 제어)란?](https://mangkyu.tistory.com/53) - [MVCC(Multi Version Concurrency Control) 알아보기](https://monday9pm.com/mvcc-multi-version-concurrency-control-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-e4102cd97e59) - [Lock Based Concurrency Control Protocol in DBMS](https://www.geeksforgeeks.org/lock-based-concurrency-control-protocol-in-dbms/) - [What is MVCC? How does multiversion concurrency control work?](https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/What-is-MVCC-How-does-Multiversion-Concurrencty-Control-work)
 

데이터베이스 인덱스에 대해서 설명해주세요.

인덱스는 데이터베이스 테이블의 검색 속도를 향상시키기 위한 자료구조로 백과사전의 색인과 같습니다. 저장되는 컬럼의 값을 사용하여 항상 정렬된 상태를 유지하는 것이 특징입니다. 이러한 특징으로 인해 인덱스는 INSERT, UPDATE, DELETE의 성능이 희생된다는 것이 단점입니다.
 
  • 인덱스는 어떤 자료 구조로 이루어져있나요? 🤔
MySQL InnoDB를 기준으로 설명드리자면, B+Tree와 같은 변형 B-Tree 자료구조를 이용해서 인덱스를 구현합니다. 기본 토대는 B-Tree 인덱스이기 때문에 이를 기준으로 설명합니다. B-Tree 인덱스는 컬럼의 값을 변형하지 않고 인덱스 구조체 내에서 항상 정렬된 상태로 유지합니다.
B-Tree(Balanced-Tree)에서는 크게 3가지 노드가 존재합니다. 최상위에 하나의 루트 노드가 존재하며, 가장 하위 노드인 리프 노드가 존재합니다. 이 두 노드의 중간에 존재하는 브랜치 노드가 존재합니다. 최하위 노드인 리프 노드에는 실제 데이터 레코드를 찾아가기 위한 주소값을 가지고 있습니다.
InnoDB 스토리지 엔진에서는 세컨더리 인덱스(프라이머리 인덱스를 제외한 모든 인덱스)의 리프 노드에는 레코드의 PK가 저장됩니다. 따라서 세컨더리 인덱스 검색에서는 레코드를 읽기 위해 PK를 가지고 있는 B-Tree를 다시 한번 검색해야합니다.
 
  • MySQL 스캔 방식은 어떤 게 있나요? 😀
MySQL에는 크게 인덱스 레인지 스캔, 인덱스 풀 스캔, 루스 인덱스 스캔 방식이 있습니다.
인덱스 레인지 스캔은 검색할 인덱스 범위가 결정되었을 경우 사용하며 가장 빠릅니다.
  • 인덱스에서 조건을 만족하는 값이 저장된 시작 리프 노드를 찾습니다.(index seek)
  • 시작 리프 노드부터 필요한 만큼 인덱스를 차례대로 읽습니다. (index scan)
  • 인덱스 키와 레코드 주소를 이용해 저장된 페이지를 가져오고 레코드를 읽어옵니다.
레코드를 읽어오는 과정에서 랜덤 IO가 발생할 수 있습니다. 읽어야할 데이터 레코드가 전체 20-25%의 경우에는 풀 테이블 스캔(순차 IO를 이용)이 더욱 좋을 수 있습니다.
인덱스 풀 스캔은 인덱스를 사용하지만 인덱스를 처음부터 끝까지 모두 읽는 방식입니다.
  • 인덱스를 ABC 순서로 만들었는데 조건절에 B 혹은 C로 검색하는 경우 사용됩니다.
  • 인덱스를 생성하는 목적은 아니지만, 그래도 풀 테이블 스캔보다는 낫습니다. (데이터 레코드까지 읽지 않는 경우)
루스 인덱스 스캔은 듬성듬성하게 인덱스를 읽는 것을 의미합니다. (앞서 언급한 인덱스 레인지, 인덱스 풀 스캔은 타이트 인덱스 스캔으로 분류됩니다.)
  • 중간에 필요하지 않은 인덱스 키 값은 무시하고 다음으로 넘어가는 형태로 처리합니다.
  • group by, max(), min() 함수에 대해 최적화하는 경우에 사용됩니다.
매일메일 - 데이터베이스 인덱스에 대해서 설명해주세요.
인덱스는 데이터베이스 테이블의 검색 속도를 향상시키기 위한 자료구조로 백과사전의 색인과 같습니다. 저장되는 컬럼의 값을 사용하여 항상 정렬된 상태를 유지하는 것이 특징입니다. 이러한 특징으로 인해 인덱스는 INSERT, UPDATE, DELETE의 성능이 희생된다는 것이 단점입니다. ## 인덱스는 어떤 자료 구조로 이루어져있나요? 🤔 MySQL InnoDB를 기준으로 설명드리자면, B+Tree와 같은 변형 B-Tree 자료구조를 이용해서 인덱스를 구현합니다. 기본 토대는 B-Tree 인덱스이기 때문에 이를 기준으로 설명합니다. B-Tree 인덱스는 컬럼의 값을 변형하지 않고 인덱스 구조체 내에서 항상 정렬된 상태로 유지합니다. B-Tree(Balanced-Tree)에서는 크게 3가지 노드가 존재합니다. 최상위에 하나의 루트 노드가 존재하며, 가장 하위 노드인 리프 노드가 존재합니다. 이 두 노드의 중간에 존재하는 브랜치 노드가 존재합니다. 최하위 노드인 리프 노드에는 실제 데이터 레코드를 찾아가기 위한 주소값을 가지고 있습니다. InnoDB 스토리지 엔진에서는 세컨더리 인덱스(프라이머리 인덱스를 제외한 모든 인덱스)의 리프 노드에는 레코드의 PK가 저장됩니다. 따라서 세컨더리 인덱스 검색에서는 레코드를 읽기 위해 PK를 가지고 있는 B-Tree를 다시 한번 검색해야합니다. ## MySQL 스캔 방식은 어떤 게 있나요? 😀 MySQL에는 크게 인덱스 레인지 스캔, 인덱스 풀 스캔, 루스 인덱스 스캔 방식이 있습니다. `인덱스 레인지 스캔`은 검색할 인덱스 범위가 결정되었을 경우 사용하며 가장 빠릅니다. - 인덱스에서 조건을 만족하는 값이 저장된 시작 리프 노드를 찾습니다.(index seek) - 시작 리프 노드부터 필요한 만큼 인덱스를 차례대로 읽습니다. (index scan) - 인덱스 키와 레코드 주소를 이용해 저장된 페이지를 가져오고 레코드를 읽어옵니다. 레코드를 읽어오는 과정에서 랜덤 IO가 발생할 수 있습니다. 읽어야할 데이터 레코드가 전체 20-25%의 경우에는 풀 테이블 스캔(순차 IO를 이용)이 더욱 좋을 수 있습니다. `인덱스 풀 스캔`은 인덱스를 사용하지만 인덱스를 처음부터 끝까지 모두 읽는 방식입니다. - 인덱스를 ABC 순서로 만들었는데 조건절에 B 혹은 C로 검색하는 경우 사용됩니다. - 인덱스를 생성하는 목적은 아니지만, 그래도 풀 테이블 스캔보다는 낫습니다. (데이터 레코드까지 읽지 않는 경우) `루스 인덱스 스캔`은 듬성듬성하게 인덱스를 읽는 것을 의미합니다. (앞서 언급한 인덱스 레인지, 인덱스 풀 스캔은 `타이트 인덱스 스캔`으로 분류됩니다.) - 중간에 필요하지 않은 인덱스 키 값은 무시하고 다음으로 넘어가는 형태로 처리합니다. - group by, max(), min() 함수에 대해 최적화하는 경우에 사용됩니다. ### 추가 학습 자료를 공유합니다. - [[Database] 인덱스(index)란?](https://mangkyu.tistory.com/96) - [[MySQL] 프라이머리 키(PK, Primary Key)에 대해 쉽고 완벽하게 이해하기](https://mangkyu.tistory.com/285) - [[MySQL] B-Tree로 인덱스(Index)에 대해 쉽고 완벽하게 이해하기](https://mangkyu.tistory.com/286) - [[10분 테코톡] 안돌의 INDEX](https://youtu.be/NkZ6r6z2pBg?feature=shared) - [[10분 테코톡] 초코칩&로키의 인덱스와 스캔 튜닝](https://youtu.be/_UI8YDU_mfg?feature=shared)
 

트랜잭션 격리수준은 무엇인가요?

트랜잭션의 격리 수준은 동시에 여러 트랜잭션이 실행될 때 한 트랜잭션이 다른 트랜잭션의 연산에 영향을 받지 않도록 하는 정도를 말합니다. 낮은 격리 수준은 동시 처리 능력을 높이지만, 데이터의 일관성 문제를 발생시킬 수 있습니다. 반면, 높은 격리 수준은 데이터의 일관성을 보장하지만, 동시 처리 능력이 떨어질 수 있습니다. 즉, 데이터 정합성과 성능은 반비례합니다. 트랜잭션 격리 수준은 개발자가 트랜잭션 격리 수준을 설정할 수 있는 기능을 제공하는 기능입니다.
 
  • 트랜잭션 격리 수준은 어떤 것이 있고 각각 어떤 특징이 있나요? 🤔
트랜잭션 격리 수준은 READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ가 존재합니다.
READ UNCOMMITTED는 커밋이 되지 않은 트랜잭션의 데이터 변경 내용을 다른 트랜잭션이 조회하는 것을 허용합니다. 또한 해당 격리 수준에서는 Dirty Read, Phantom Read, Non-Repeatable Read 문제가 발생할 수 있습니다.
READ COMMITTED는 커밋이 완료된 트랜잭션의 변경사항만 다른 트랜잭션에서 조회할 수 있도록 허용합니다. 특정 트랜잭션이 이루어지는 동안, 다른 트랜잭션은 해당 데이터에 접근할 수 없습니다. Dirty Read는 발생하지 않지만, Phantom Read, Non-Repeatable Read 문제가 발생할 수 있습니다.
REPEATABLE READ는 한 트랜잭션에서 특정 레코드를 조회할 때 항상 같은 데이터를 응답하는 것을 보장합니다. 하지만, SERIALIZABLE과 다르게 행이 추가되는 것을 막지는 않습니다. Non-Repeatable Read 문제가 발생하지 않지만, Phantom Read 문제가 발생할 수 있습니다.
SERIALIZABLE은 특정 트랜잭션이 사용중인 테이블의 모든 행을 다른 트랜잭션이 접근할 수 없도록 잠급니다. 가장 높은 데이터 정합성을 가지지만 성능이 가장 낮습니다. MySQL의 경우 단순한 SELECT 쿼리가 실행되더라도 데이터베이스 잠금이 걸려 다른 트랜잭션에서 데이터에 접근할 수 없습니다.
 
  • 발생하는 문제를 기준으로 설명을 잘해주셨네요. 그런데 각 문제들은 어떤 문제들인가요? 🤓
Dirty Read는 한 트랜잭션이 다른 트랜잭션이 변경 중인 데이터를 읽는 경우 발생합니다. 다른 트랜잭션이 아직 커밋되지 않은 (즉, 롤백할 가능성이 있는) 데이터를 읽어서, 그 데이터가 나중에 롤백될 경우 트랜잭션의 결과가 변경될 수 있습니다. 이는 데이터의 일관성을 깨뜨릴 수 있습니다.
Phantom Read는 한 트랜잭션이 동일한 쿼리를 두 번 실행했을 때, 두 번의 쿼리 사이에 다른 트랜잭션이 삽입, 갱신, 삭제 등의 작업을 수행하여 결과 집합이 달라지는 경우를 말합니다. 이로 인해 한 트랜잭션 내에서 일관성 없는 결과를 가져올 수 있습니다.
Non-Repeatable Read는 같은 트랜잭션 안에서 동일한 쿼리를 실행했을 때, 다른 결과를 얻는 경우를 의미합니다. 예를 들어, 한 트랜잭션이 같은 데이터를 두 번 읽을 때, 첫 번째 읽기와 두 번째 읽기 사이에 다른 트랜잭션이 해당 데이터를 변경했을 경우 발생할 수 있습니다.
매일메일 - 트랜잭션 격리수준은 무엇인가요?
트랜잭션의 격리 수준은 동시에 여러 트랜잭션이 실행될 때 한 트랜잭션이 다른 트랜잭션의 연산에 영향을 받지 않도록 하는 정도를 말합니다. 낮은 격리 수준은 동시 처리 능력을 높이지만, 데이터의 일관성 문제를 발생시킬 수 있습니다. 반면, 높은 격리 수준은 데이터의 일관성을 보장하지만, 동시 처리 능력이 떨어질 수 있습니다. 즉, 데이터 정합성과 성능은 반비례합니다. 트랜잭션 격리 수준은 개발자가 트랜잭션 격리 수준을 설정할 수 있는 기능을 제공하는 기능입니다. ## 트랜잭션 격리 수준은 어떤 것이 있고 각각 어떤 특징이 있나요? 🤔 트랜잭션 격리 수준은 READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ가 존재합니다. READ UNCOMMITTED는 커밋이 되지 않은 트랜잭션의 데이터 변경 내용을 다른 트랜잭션이 조회하는 것을 허용합니다. 또한 해당 격리 수준에서는 Dirty Read, Phantom Read, Non-Repeatable Read 문제가 발생할 수 있습니다. READ COMMITTED는 커밋이 완료된 트랜잭션의 변경사항만 다른 트랜잭션에서 조회할 수 있도록 허용합니다. 특정 트랜잭션이 이루어지는 동안, 다른 트랜잭션은 해당 데이터에 접근할 수 없습니다. Dirty Read는 발생하지 않지만, Phantom Read, Non-Repeatable Read 문제가 발생할 수 있습니다. REPEATABLE READ는 한 트랜잭션에서 특정 레코드를 조회할 때 항상 같은 데이터를 응답하는 것을 보장합니다. 하지만, SERIALIZABLE과 다르게 행이 추가되는 것을 막지는 않습니다. Non-Repeatable Read 문제가 발생하지 않지만, Phantom Read 문제가 발생할 수 있습니다. SERIALIZABLE은 특정 트랜잭션이 사용중인 테이블의 모든 행을 다른 트랜잭션이 접근할 수 없도록 잠급니다. 가장 높은 데이터 정합성을 가지지만 성능이 가장 낮습니다. MySQL의 경우 단순한 SELECT 쿼리가 실행되더라도 데이터베이스 잠금이 걸려 다른 트랜잭션에서 데이터에 접근할 수 없습니다. ## 발생하는 문제를 기준으로 설명을 잘해주셨네요. 그런데 각 문제들은 어떤 문제들인가요? 🤓 Dirty Read는 한 트랜잭션이 다른 트랜잭션이 변경 중인 데이터를 읽는 경우 발생합니다. 다른 트랜잭션이 아직 커밋되지 않은 (즉, 롤백할 가능성이 있는) 데이터를 읽어서, 그 데이터가 나중에 롤백될 경우 트랜잭션의 결과가 변경될 수 있습니다. 이는 데이터의 일관성을 깨뜨릴 수 있습니다. Phantom Read는 한 트랜잭션이 동일한 쿼리를 두 번 실행했을 때, 두 번의 쿼리 사이에 다른 트랜잭션이 삽입, 갱신, 삭제 등의 작업을 수행하여 결과 집합이 달라지는 경우를 말합니다. 이로 인해 한 트랜잭션 내에서 일관성 없는 결과를 가져올 수 있습니다. Non-Repeatable Read는 같은 트랜잭션 안에서 동일한 쿼리를 실행했을 때, 다른 결과를 얻는 경우를 의미합니다. 예를 들어, 한 트랜잭션이 같은 데이터를 두 번 읽을 때, 첫 번째 읽기와 두 번째 읽기 사이에 다른 트랜잭션이 해당 데이터를 변경했을 경우 발생할 수 있습니다. ### 추가 학습 자료를 공유합니다. - [[MySQL] 트랜잭션의 격리 수준(Isolation Level)에 대해 쉽고 완벽하게 이해하기](https://mangkyu.tistory.com/299) - [wiki - Isolation (database systems)](https://en.wikipedia.org/wiki/Isolation_(database_systems)) - [MySQL의 팬텀 리드를 재연하기](http://stackoverflow.com/questions/42794425/unable-to-produce-a-phantom-read/42796969#42796969) - [[10분 테코톡] 러쉬의 MySQL 트랜잭션 격리 수준](https://youtu.be/QHWwNTGkwAU?si=AMYGpIry6nCPosuu)
 

공유 락과 배타 락에 대해서 설명해주세요.

DBMS에서 트랜잭션을 특별한 제어 없이 병행 수행을 허용한다면 데이터의 일관성과 무결성을 보장하기 어려울 수 있습니다. 이때, 병행 수행되는 트랜잭션들을 제어하기 위해서 락을 사용할 수 있으며 DBMS에서 락은 크게 공유 락과 배타 락으로 분류할 수 있습니다.
공유 락(Shared Lock) 은 읽기 락(Read Lock)이라고 부르며, 공유 락이 걸린 데이터에 대해서 다른 트랜잭션에서도 공유 락을 획득할 수 있지만, 배타 락은 획득할 수 없습니다. 즉, 공유 락을 사용하면 트랜잭션 내에서 조회한 데이터가 변경되지 않는다는 것을 보장합니다.
SELECT * FROM table_name WHERE id = 1 FOR SHARE;
배타 락(Exclusive Lock) 은 쓰기 락(Write Lock)이라고 부르며, 배타 락이 걸린 데이터에 대해서 다른 트랜잭션에서는 공유 락과 배타 락을 획득할 수 없습니다. 즉, 배타 락을 획득한 트랜잭션은 데이터에 대한 독점권을 가집니다.
SELECT * FROM table_name WHERE id = 1 FOR UPDATE;
정리하자면, 공유 락이 걸린 데이터는 다른 트랜잭션에서 공유 락을 획득 할 수 있고, 배타 락이 걸린 데이터는 다른 트랜잭션에서 어떤 종류의 락도 획득할 수 없어서 대기하는 상황이 발생할 수 있습니다.
 
  • 데드 락은 언제 발생하며 어떻게 해결할 수 있나요? 🤔
데드 락(Dead Lock) 이란 교착 상태로, 두개 이상의 트랜잭션이 서로 필요로 하는 데이터의 락을 점유하고 있어서 무한히 대기하는 상황을 말합니다. 트랜잭션은 락을 획득하지 못하는 경우, 다른 트랜잭션이 점유하고 있는 락이 해제될 때까지 대기합니다. 예를 들어, 다음과 같은 트랜잭션들이 존재한다고 가정하겠습니다.
  • 트랜잭션 A, B가 있고 id가 1, 2인 데이터가 있는 상황에 두 트랜잭션이 시작합니다.
  • 트랜잭션 A는 id 1번을 읽고, 2번 데이터를 변경하는 트랜잭션입니다.
  • 트랜잭션 B는 id 2번을 읽고 1번을 변경하는 트랜잭션입니다.
이때 다음과 같은 상황에서 데드 락이 발생할 수 있습니다.
  • A는 1번, B는 2번 데이터에 대해 공유 락을 획득합니다.
  • A는 2번 데이터의 공유 락을 가지고 있는 B 트랜잭션이 락을 해제할 때까지 대기합니다.
  • B는 1번 데이터의 공유 락을 가지고 있는 A 트랜잭션이 락을 해제할 때까지 대기합니다.
데드 락을 해결하기 위해서 트랜잭션에서 락 획득 순서를 일관되게 할 수 있습니다. 모든 트랜잭션에서 1번 데이터, 2번 데이터 순으로 락을 획득할 시 데드 락이 발생하지 않습니다. 혹은 락 타임 아웃을 설정하여 데드 락 상황을 해결할 수 있습니다.
매일메일 - 공유 락과 배타 락에 대해서 설명해주세요.
DBMS에서 트랜잭션을 특별한 제어 없이 병행 수행을 허용한다면 데이터의 일관성과 무결성을 보장하기 어려울 수 있습니다. 이때, 병행 수행되는 트랜잭션들을 제어하기 위해서 락을 사용할 수 있으며 DBMS에서 락은 크게 공유 락과 배타 락으로 분류할 수 있습니다. **공유 락(Shared Lock)** 은 읽기 락(Read Lock)이라고 부르며, 공유 락이 걸린 데이터에 대해서 다른 트랜잭션에서도 공유 락을 획득할 수 있지만, 배타 락은 획득할 수 없습니다. 즉, 공유 락을 사용하면 트랜잭션 내에서 조회한 데이터가 변경되지 않는다는 것을 보장합니다. ```sql SELECT * FROM table_name WHERE id = 1 FOR SHARE; ``` **배타 락(Exclusive Lock)** 은 쓰기 락(Write Lock)이라고 부르며, 배타 락이 걸린 데이터에 대해서 다른 트랜잭션에서는 공유 락과 배타 락을 획득할 수 없습니다. 즉, 배타 락을 획득한 트랜잭션은 데이터에 대한 독점권을 가집니다. ```sql SELECT * FROM table_name WHERE id = 1 FOR UPDATE; ``` 정리하자면, 공유 락이 걸린 데이터는 다른 트랜잭션에서 공유 락을 획득 할 수 있고, 배타 락이 걸린 데이터는 다른 트랜잭션에서 어떤 종류의 락도 획득할 수 없어서 대기하는 상황이 발생할 수 있습니다. ## 데드 락은 언제 발생하며 어떻게 해결할 수 있나요? 🤔 **데드 락(Dead Lock)** 이란 교착 상태로, 두개 이상의 트랜잭션이 서로 필요로 하는 데이터의 락을 점유하고 있어서 무한히 대기하는 상황을 말합니다. 트랜잭션은 락을 획득하지 못하는 경우, 다른 트랜잭션이 점유하고 있는 락이 해제될 때까지 대기합니다. 예를 들어, 다음과 같은 트랜잭션들이 존재한다고 가정하겠습니다. - 트랜잭션 A, B가 있고 id가 1, 2인 데이터가 있는 상황에 두 트랜잭션이 시작합니다. - 트랜잭션 A는 id 1번을 읽고, 2번 데이터를 변경하는 트랜잭션입니다. - 트랜잭션 B는 id 2번을 읽고 1번을 변경하는 트랜잭션입니다. 이때 다음과 같은 상황에서 데드 락이 발생할 수 있습니다. - A는 1번, B는 2번 데이터에 대해 공유 락을 획득합니다. - A는 2번 데이터의 공유 락을 가지고 있는 B 트랜잭션이 락을 해제할 때까지 대기합니다. - B는 1번 데이터의 공유 락을 가지고 있는 A 트랜잭션이 락을 해제할 때까지 대기합니다. 데드 락을 해결하기 위해서 트랜잭션에서 락 획득 순서를 일관되게 할 수 있습니다. 모든 트랜잭션에서 1번 데이터, 2번 데이터 순으로 락을 획득할 시 데드 락이 발생하지 않습니다. 혹은 락 타임 아웃을 설정하여 데드 락 상황을 해결할 수 있습니다. ## 추가 학습 자료를 공유합니다. - [MySQL 8.0의 공유 락(Shared Lock)과 배타 락(Exclusive Lock)](https://hudi.blog/mysql-8.0-shared-lock-and-exclusive-lock/) - [JPA의 비관적 락, MySQL 8.0 공유락과 베타락을 통한 동시성 제어](https://haon.blog/haon/jpa/pemistic-lock/) - [Shared and Exclusive Locks](https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-shared-exclusive-locks) - [[10분 테코톡] ⛲️ 오즈의 데이터베이스 Lock](https://youtu.be/onBpJRDSZGA?si=UmGmBkVKYKO6-nsS)
 

데이터베이스 커넥션 풀(Connection Pool)을 사용하지 않으면 어떤 문제가 발생할 수 있나요?

애플리케이션과 데이터베이스가 통신을 하기 위해서는 데이터베이스 커넥션이 필요합니다.
데이터베이스 커넥션의 생애주기 :
  1. 데이터베이스 드라이버를 사용하여 데이터베이스에 연결
  1. 데이터 읽기/쓰기를 위한 TCP 소켓 열기
  1. 소켓을 통한 데이터 읽기/쓰기
  1. 연결 종료
  1. 소켓 닫기
커넥션 풀이 없다면 애플리케이션에서 데이터베이스에 접근해야하는 요청을 처리할 때마다 커넥션을 새로 생성하여 연결하고 해제하는 과정을 반복해야 합니다. 이 과정은 비용이 상당히 많이 들기 때문에 요청의 응답시간이 길어집니다.
또 동시에 많은 요청이 들어올 경우 매번 새로운 커넥션을 생성하게 되는데, 데이터베이스의 최대 연결 수를 초과할 수 있습니다. 데이터베이스는 일반적으로 동시에 처리할 수 있는 요청 개수에 제한이 있는데, 이 제한을 초과하면 요청이 거부되어 사라지거나, 데이터베이스 자체가 비정상 종료될 수 있습니다.
 
  • 데이터베이스 커넥션 풀을 사용함으로써 얻을 수 있는 장점은 무엇인가요?
커넥션 풀(Connection Pool)은 애플리케이션과 데이터베이스 간의 데이터베이스 연결(Connection)을 미리 생성해두고, 이를 재사용하는 기법을 말합니다. 데이터베이스에 접근할 때마다 새로운 연결을 생성하고 종료하는 대신, 미리 준비된 연결을 재사용함으로써 성능을 향상시키고 자원 사용을 최적화할 수 있습니다.
커넥션 풀의 주요 구성 요소는 초기 풀 크기(Initial Pool Size), 최소 풀 크기(Minimum Pool Size), 최대 풀 크기(Maximum Pool Size), 연결 대기 시간(Connection Timeout) 등이 있고, 이를 통해 커넥션을 효율적으로 관리하고 사용할 수 있습니다.
 
  • 그럼 커넥션 풀 사이즈는 클 수록 좋나요? 🤔
커넥션을 사용하는 주체는 스레드(Thread)이기 때문에, 커넥션과 스레드를 연결지어 생각해야 합니다. 만약 커넥션 풀 사이즈가 스레드 풀 사이즈보다 크면, 스레드가 모두 사용하지 못해서 리소스가 낭비됩니다. 반대로 커넥션 풀 사이즈가 스레드 풀 사이즈보다 작으면, 스레드가 커넥션이 반환되기를 기다려야 하기 때문에 작업이 지연됩니다.
커넥션 풀 사이즈와 스레드 풀 사이즈의 균형이 맞더라도, 너무 큰 사이즈로 설정하면, 데이터베이스 서버, 애플리케이션 서버의 메모리와 CPU를 과도하게 사용하게 되므로 성능이 저하됩니다.
매일메일 - 데이터베이스 커넥션 풀(Connection Pool)을 사용하지 않으면 어떤 문제가 발생할 수 있나요?
애플리케이션과 데이터베이스가 통신을 하기 위해서는 데이터베이스 커넥션이 필요합니다. 데이터베이스 커넥션의 생애주기 : 1. 데이터베이스 드라이버를 사용하여 데이터베이스에 연결 2. 데이터 읽기/쓰기를 위한 [TCP 소켓](https://en.wikipedia.org/wiki/Network_socket) 열기 3. 소켓을 통한 데이터 읽기/쓰기 4. 연결 종료 5. 소켓 닫기 커넥션 풀이 없다면 애플리케이션에서 데이터베이스에 접근해야하는 요청을 처리할 때마다 커넥션을 새로 생성하여 연결하고 해제하는 과정을 반복해야 합니다. 이 과정은 비용이 상당히 많이 들기 때문에 요청의 응답시간이 길어집니다. 또 동시에 많은 요청이 들어올 경우 매번 새로운 커넥션을 생성하게 되는데, 데이터베이스의 최대 연결 수를 초과할 수 있습니다. 데이터베이스는 일반적으로 동시에 처리할 수 있는 요청 개수에 제한이 있는데, 이 제한을 초과하면 요청이 거부되어 사라지거나, 데이터베이스 자체가 비정상 종료될 수 있습니다. ## 데이터베이스 커넥션 풀을 사용함으로써 얻을 수 있는 장점은 무엇인가요? 커넥션 풀(Connection Pool)은 애플리케이션과 데이터베이스 간의 데이터베이스 연결(Connection)을 미리 생성해두고, 이를 재사용하는 기법을 말합니다. 데이터베이스에 접근할 때마다 새로운 연결을 생성하고 종료하는 대신, 미리 준비된 연결을 재사용함으로써 성능을 향상시키고 자원 사용을 최적화할 수 있습니다. 커넥션 풀의 주요 구성 요소는 초기 풀 크기(Initial Pool Size), 최소 풀 크기(Minimum Pool Size), 최대 풀 크기(Maximum Pool Size), 연결 대기 시간(Connection Timeout) 등이 있고, 이를 통해 커넥션을 효율적으로 관리하고 사용할 수 있습니다. ## 그럼 커넥션 풀 사이즈는 클 수록 좋나요? 🤔 커넥션을 사용하는 주체는 스레드(Thread)이기 때문에, 커넥션과 스레드를 연결지어 생각해야 합니다. 만약 커넥션 풀 사이즈가 스레드 풀 사이즈보다 크면, 스레드가 모두 사용하지 못해서 리소스가 낭비됩니다. 반대로 커넥션 풀 사이즈가 스레드 풀 사이즈보다 작으면, 스레드가 커넥션이 반환되기를 기다려야 하기 때문에 작업이 지연됩니다. 커넥션 풀 사이즈와 스레드 풀 사이즈의 균형이 맞더라도, 너무 큰 사이즈로 설정하면, 데이터베이스 서버, 애플리케이션 서버의 메모리와 CPU를 과도하게 사용하게 되므로 성능이 저하됩니다. ## 추가 학습 자료를 공유합니다. - [A Simple Guide to Connection Pooling in Java](https://www.baeldung.com/java-connection-pooling) - [HikariCP](https://github.com/brettwooldridge/HikariCP) - [데이터베이스 커넥션 풀의 이해와 최적화 전략](https://f-lab.kr/insight/understanding-database-connection-pool) - [데이터베이스 커넥션 풀 (Connection Pool)과 HikariCP](https://hudi.blog/dbcp-and-hikaricp/) - [DBCP (DB connection pool)의 개념부터 설정 방법까지! hikariCP와 MySQL을 예제로 설명합니다!](https://youtu.be/zowzVqx3MQ4?si=NxuDRUs6SurARRx-)
 

HTTP 메서드에서 멱등성이란 무엇인가요?

연산을 여러 번 적용하더라도 결과가 달라지지 않는 성질을 멱등성이라고 합니다. HTTP 메서드의 멱등성은 동일한 요청을 한번 보내는 것과 여러번 보내는 것이 서로 동일한 효과를 지니며, 서버의 상태도 동일하게 남을 경우에 멱등하다고 이야기할 수 있습니다. 대표적으로 멱등한 메서드는 GET, HEAD, PUT, DELETE, TRACE, OPTIONS 가 있습니다.
 
  • 멱등성은 어떻게 활용될 수 있나요? 🤔
모종의 이유로 전송 커넥션이 끊어졌을 때, 멱등성은 클라이언트가 다시 같은 요청을 해도 되는가에 대한 판단 근거가 될 수 있습니다. 멱등하다면 요청을 재시도할 때 같은 서버의 상태를 보장하기 때문에 문제가 없습니다. 반면, 멱등하지 않다면 재시도 요청시 중복 요청을 보내 문제를 발생 시킬 수 있습니다. 예를 들어, 사용자가 결제하는 시점에 타임아웃으로 인해 정상 응답을 못받는 상황을 생각해 볼 수 있습니다. 해당 경우에서 멱등하지 않은 결제 API 경우에는 결제가 성공했는지 수동으로 확인하고 재요청해야합니다. 반면, 멱등한 결제 API의 경우에는 안심하고 여러 번 요청할 수 있으며 중복 요청으로 발생하는 문제(중복 결제)를 방지할 수 있습니다.
매일메일 - HTTP 메서드에서 멱등성이란 무엇인가요?
연산을 여러 번 적용하더라도 결과가 달라지지 않는 성질을 멱등성이라고 합니다. HTTP 메서드의 멱등성은 동일한 요청을 한번 보내는 것과 여러번 보내는 것이 서로 동일한 효과를 지니며, 서버의 상태도 동일하게 남을 경우에 멱등하다고 이야기할 수 있습니다. 대표적으로 멱등한 메서드는 GET, HEAD, PUT, DELETE, TRACE, OPTIONS 가 있습니다. ## 멱등성은 어떻게 활용될 수 있나요? 🤔 모종의 이유로 전송 커넥션이 끊어졌을 때, 멱등성은 클라이언트가 다시 같은 요청을 해도 되는가에 대한 판단 근거가 될 수 있습니다. 멱등하다면 요청을 재시도할 때 같은 서버의 상태를 보장하기 때문에 문제가 없습니다. 반면, 멱등하지 않다면 재시도 요청시 중복 요청을 보내 문제를 발생 시킬 수 있습니다. 예를 들어, 사용자가 결제하는 시점에 타임아웃으로 인해 정상 응답을 못받는 상황을 생각해 볼 수 있습니다. 해당 경우에서 멱등하지 않은 결제 API 경우에는 결제가 성공했는지 수동으로 확인하고 재요청해야합니다. 반면, 멱등한 결제 API의 경우에는 안심하고 여러 번 요청할 수 있으며 중복 요청으로 발생하는 문제(중복 결제)를 방지할 수 있습니다. ## 추가 학습 자료를 공유합니다. - [토스페이먼츠 - 멱등성이 뭔가요?](https://velog.io/@tosspayments/%EB%A9%B1%EB%93%B1%EC%84%B1%EC%9D%B4-%EB%AD%94%EA%B0%80%EC%9A%94) - [[HTTP] HTTP 메소드의 멱등성(Idempotence)과 Delete 메소드가 멱등한 이유](https://mangkyu.tistory.com/251) - [HTTP 멱등성(idempotency):개념에서 적용까지](https://rice-honey.tistory.com/8)
 

데이터베이스 시스템에서 동시성을 제어하는 방법에 대해 설명해주세요.

대표적인 동시성 제어 방식으로 MVCC(Multi-Version Concurrency Control)Lock-Based Concurrency Control이 있습니다.
 
  • MVCC(Multi-Version Concurrency Control)
MVCC는 데이터의 여러 버전을 유지하여 트랜잭션이 동시에 데이터를 읽고 쓸 수 있도록 하는 방식입니다. 각 트랜잭션은 자신만의 일관된 스냅샷을 기반으로 데이터를 읽어, 다른 트랜잭션의 변경 사항에 영향을 받지 않습니다.
데이터의 각 버전을 유지하여 읽기 작업이 쓰기 작업과 독립적으로 이루어질 수 있습니다. 트랜잭션은 시작 시점의 스냅샷을 기반으로 데이터를 읽어, 다른 트랜잭션의 변경 사항을 보지 못합니다.
또한 읽기 작업 시 잠금을 사용하지 않아 높은 동시성을 제공합니다. 읽기 작업이 잠금에 의해 지연되지 않아, 읽기 중심의 애플리케이션에서 우수한 성능을 보입니다. 읽기 작업 시 잠금을 사용하지 않으므로, 쓰기 작업과의 충돌이 줄어듭니다. 하지만 여러 버전의 데이터를 유지해야 하므로 저장 공간이 더 많이 필요할 수 있습니다.
트랜잭션이 시작된 시점의 데이터 상태를 기반으로 읽기 작업을 수행하여 일관성을 유지합니다. 또 갭락과 넥스트키 락을 통해 팬텀 리드를 방지합니다.
 
  • Lock-Based Concurrency Control
Lock-Based 방식은 데이터에 접근할 때 잠금(Lock) 을 사용하여 동시성을 제어합니다. 트랜잭션이 데이터를 읽거나 수정할 때 해당 데이터에 잠금을 걸어 다른 트랜잭션의 접근을 제한합니다. 즉, 잠금을 통해 데이터의 일관성과 무결성을 직접적으로 제어합니다.
데이터에 접근할 때 잠금을 걸어 다른 트랜잭션의 접근을 제한합니다. 읽기 작업은 공유 잠금을, 쓰기 작업은 배타 잠금을 사용하여 동시성을 제어합니다. 많은 다수의 트랜잭션이 동일한 데이터에 접근할 경우 성능 저하가 발생할 수 있습니다. 또 잘못된 잠금 순서나 설계로 인해 교착 상태(Deadlock)가 발생할 위험이 있습니다.
 
  • MVCC와 Lock-Based Concurrency Control 둘 중 어떤 걸 사용해야 하나요? 🤔
실제 데이터베이스 시스템, 특히 MySQL의 InnoDB는 MVCC와 Lock-Based 방식의 장점을 결합하여 동시성 제어를 최적화합니다.
읽기 트랜잭션은 MVCC를 사용하여 일관된 스냅샷을 기반으로 데이터를 읽으므로, 잠금을 최소화하고 높은 동시성을 유지할 수 있습니다.
쓰기 트랜잭션은 잠금을 사용하여 데이터의 일관성과 무결성을 유지하면서, 동시에 데이터 충돌을 방지합니다.
매일메일 - 데이터베이스 시스템에서 동시성을 제어하는 방법에 대해 설명해주세요.
대표적인 동시성 제어 방식으로 **MVCC(Multi-Version Concurrency Control)** 와 **Lock-Based Concurrency Control**이 있습니다. ## MVCC(Multi-Version Concurrency Control) MVCC는 데이터의 여러 버전을 유지하여 트랜잭션이 동시에 데이터를 읽고 쓸 수 있도록 하는 방식입니다. 각 트랜잭션은 자신만의 **일관된 스냅샷**을 기반으로 데이터를 읽어, 다른 트랜잭션의 변경 사항에 영향을 받지 않습니다. 데이터의 각 버전을 유지하여 읽기 작업이 쓰기 작업과 독립적으로 이루어질 수 있습니다. 트랜잭션은 시작 시점의 스냅샷을 기반으로 데이터를 읽어, 다른 트랜잭션의 변경 사항을 보지 못합니다. 또한 **읽기 작업 시 잠금을 사용하지 않아 높은 동시성**을 제공합니다. 읽기 작업이 잠금에 의해 지연되지 않아, 읽기 중심의 애플리케이션에서 우수한 성능을 보입니다. 읽기 작업 시 잠금을 사용하지 않으므로, 쓰기 작업과의 충돌이 줄어듭니다. 하지만 여러 버전의 데이터를 유지해야 하므로 저장 공간이 더 많이 필요할 수 있습니다. 트랜잭션이 시작된 시점의 데이터 상태를 기반으로 읽기 작업을 수행하여 일관성을 유지합니다. 또 갭락과 넥스트키 락을 통해 팬텀 리드를 방지합니다. ## Lock-Based Concurrency Control Lock-Based 방식은 데이터에 접근할 때 **잠금(Lock)** 을 사용하여 동시성을 제어합니다. 트랜잭션이 데이터를 읽거나 수정할 때 해당 데이터에 잠금을 걸어 다른 트랜잭션의 접근을 제한합니다. 즉, 잠금을 통해 데이터의 일관성과 무결성을 직접적으로 제어합니다. 데이터에 접근할 때 잠금을 걸어 다른 트랜잭션의 접근을 제한합니다. **읽기 작업은 공유 잠금**을, **쓰기 작업은 배타 잠금**을 사용하여 동시성을 제어합니다. 많은 다수의 트랜잭션이 동일한 데이터에 접근할 경우 성능 저하가 발생할 수 있습니다. 또 잘못된 잠금 순서나 설계로 인해 교착 상태(Deadlock)가 발생할 위험이 있습니다. ## MVCC와 Lock-Based Concurrency Control 둘 중 어떤 걸 사용해야 하나요? 🤔 실제 데이터베이스 시스템, 특히 MySQL의 InnoDB는 MVCC와 Lock-Based 방식의 장점을 결합하여 동시성 제어를 최적화합니다. **읽기 트랜잭션은 MVCC를 사용**하여 일관된 스냅샷을 기반으로 데이터를 읽으므로, **잠금을 최소화**하고 **높은 동시성**을 유지할 수 있습니다. **쓰기 트랜잭션은 잠금을 사용**하여 **데이터의 일관성과 무결성을 유지**하면서, 동시에 **데이터 충돌을 방지**합니다. ## 추가 학습 자료를 공유합니다. - [[Database] MVCC(다중 버전 동시성 제어)란?](https://mangkyu.tistory.com/53) - [MVCC(Multi Version Concurrency Control) 알아보기](https://monday9pm.com/mvcc-multi-version-concurrency-control-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-e4102cd97e59) - [Lock Based Concurrency Control Protocol in DBMS](https://www.geeksforgeeks.org/lock-based-concurrency-control-protocol-in-dbms/) - [What is MVCC? How does multiversion concurrency control work?](https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/What-is-MVCC-How-does-Multiversion-Concurrencty-Control-work)
 

MySQL InnoDB에서 갭락과 넥스트키 락이란 무엇이며, 어떻게 팬텀 리드를 방지하나요?

  • Phantom Read란 무엇인가요?
Phantom Read는 트랜잭션이 동일한 조건의 쿼리를 반복 실행할 때, 나중에 실행된 쿼리에서 처음에는 존재하지 않았던 새로운 행이 나타나는 현상을 말합니다. 이는 주로 읽기 일관성(Read Consistency) 을 유지하는 과정에서 발생할 수 있는 문제로, 데이터의 삽입이나 삭제가 다른 트랜잭션에 의해 이루어질 때 발생합니다.
-- 트랜잭션 A 시작 START TRANSACTION; -- 트랜잭션 A 첫 번째 조회 SELECT * FROM orders WHERE amount > 150; -- 트랜잭션 B 시작 START TRANSACTION; -- 트랜잭션 B 새로운 행 삽입 INSERT INTO orders (customer_id, amount) VALUES (4, 250); -- 트랜잭션 B 커밋 COMMIT; -- 동일한 조건으로 트랜잭션 A 두 번째 조회시, 트랜잭션 A의 첫 번째 조회에는 존재하지 않던, 트랜잭션 B에서 삽입된 새로운 행이 함께 조회된다. -- 단, MVCC를 지원하는 경우 해당 케이스에서 팬텀 리드가 발생하지 않는다. SELECT * FROM orders WHERE amount > 150;
 
  • 갭락(Gap Lock)이란?
갭 락은 특정 인덱스 값 사이의 공간을 잠그는 락입니다. 기존 레코드 간의 간격을 보호하여 새로운 레코드의 삽입을 방지합니다. 갭 락은 범위 내에 특정 레코드가 존재하지 않을 때 적용됩니다. 트랜잭션이 특정 범위 내에서 데이터의 삽입을 막아 팬텀 읽기(Phantom Read) 현상을 방지합니다. 예를 들어, 인덱스 값 10과 20 사이의 갭을 잠그면 이 범위 내에 새로운 레코드 15를 추가할 수 없습니다.
-- id 1, 3, 5가 저장된 orders 테이블 -- 트랜잭션 A 시작 START TRANSACTION; -- 트랜잭션 A 1-3과 3-5 사이의 갭과 3 레코드 락 설정(넥스트키 락) SELECT * FROM orders WHERE orders_id BETWEEN 2 AND 4 FOR UPDATE; -- 트랜잭션 B 시작 START TRANSACTION; -- 트랜잭션 B가 id 4에 데이터 삽입 시도 시, 갭락으로 인해 삽입이 차단되어 대기 INSERT INTO orders (orders_id, orders_amount) VALUES (4, 200); ...
 
  • 넥스트키 락(Next-Key Lock)이란?
넥스트키 락레코드 락갭락을 결합한 형태로, 특정 인덱스 레코드와 그 주변의 갭을 동시에 잠그는 락입니다. 이를 통해 레코드 자체의 변경과 함께 그 주변 공간의 변경도 동시에 제어할 수 있습니다.
넥스트키 락은 특정 레코드와 그 주변 공간을 잠그기 때문에, 다른 트랜잭션이 새로운 레코드를 삽입하여 팬텀 리드를 발생시키는 것을 방지합니다.
orders_id
orders_amount
1
100
2
200
3
300
-- 트랜잭션 A 시작 START TRANSACTION; -- 트랜잭션 A amount = 200인 orders_id = 2 레코드에 대한 레코드 락과 1-2, 2-3에 대한 갭락을 동시에 잠금으로써 넥스트키 락을 설정 SELECT * FROM orders WHERE orders_amount = 200 FOR UPDATE; -- 트랜잭션 B 시작 START TRANSACTION; -- 트랜잭션 B orders_id = 4, orders_amount = 200인 레코드 삽입 시도 시, 넥스트키 락으로 인해 차단되어 대기 INSERT INTO orders (orders_id, order_amount) VALUES (4, 200); ...
 
  • 갭락과 넥스트키 락을 통한 팬텀 리드 방지 메커니즘
트랜잭션 A가 특정 범위의 데이터를 조회할 때, 해당 범위에 대해 갭락 또는 넥스트키 락을 설정합니다. 락이 설정된 범위 내에서는 트랜잭션 B가 새로운 레코드를 삽입하거나 기존 레코드를 수정하는 것이 차단됩니다. 따라서, 트랜잭션 A가 다시 동일한 조건으로 조회를 수행하더라도, 트랜잭션 B에 의해 새로운 데이터가 삽입되지 않아 팬텀 리드가 발생하지 않습니다.
매일메일 - MySQL InnoDB에서 갭락과 넥스트키 락이란 무엇이며, 어떻게 팬텀 리드를 방지하나요?
## Phantom Read란 무엇인가요? Phantom Read는 트랜잭션이 동일한 조건의 쿼리를 반복 실행할 때, 나중에 실행된 쿼리에서 처음에는 존재하지 않았던 새로운 행이 나타나는 현상을 말합니다. 이는 주로 **읽기 일관성(Read Consistency)** 을 유지하는 과정에서 발생할 수 있는 문제로, 데이터의 삽입이나 삭제가 다른 트랜잭션에 의해 이루어질 때 발생합니다. ```sql -- 트랜잭션 A 시작 START TRANSACTION; -- 트랜잭션 A 첫 번째 조회 SELECT * FROM orders WHERE amount > 150; -- 트랜잭션 B 시작 START TRANSACTION; -- 트랜잭션 B 새로운 행 삽입 INSERT INTO orders (customer_id, amount) VALUES (4, 250); -- 트랜잭션 B 커밋 COMMIT; -- 동일한 조건으로 트랜잭션 A 두 번째 조회시, 트랜잭션 A의 첫 번째 조회에는 존재하지 않던, 트랜잭션 B에서 삽입된 새로운 행이 함께 조회된다. -- 단, MVCC를 지원하는 경우 해당 케이스에서 팬텀 리드가 발생하지 않는다. SELECT * FROM orders WHERE amount > 150; ``` ## 갭락(Gap Lock)이란? 갭 락은 특정 인덱스 값 사이의 **공간**을 잠그는 락입니다. 기존 레코드 간의 간격을 보호하여 새로운 레코드의 삽입을 방지합니다. 갭 락은 **범위 내에 특정 레코드가 존재하지 않을 때** 적용됩니다. 트랜잭션이 특정 범위 내에서 데이터의 삽입을 막아 **팬텀 읽기(Phantom Read)** 현상을 방지합니다. 예를 들어, 인덱스 값 10과 20 사이의 갭을 잠그면 이 범위 내에 새로운 레코드 15를 추가할 수 없습니다. ```sql -- id 1, 3, 5가 저장된 orders 테이블 -- 트랜잭션 A 시작 START TRANSACTION; -- 트랜잭션 A 1-3과 3-5 사이의 갭과 3 레코드 락 설정(넥스트키 락) SELECT * FROM orders WHERE orders_id BETWEEN 2 AND 4 FOR UPDATE; -- 트랜잭션 B 시작 START TRANSACTION; -- 트랜잭션 B가 id 4에 데이터 삽입 시도 시, 갭락으로 인해 삽입이 차단되어 대기 INSERT INTO orders (orders_id, orders_amount) VALUES (4, 200); ... ``` ## 넥스트키 락(Next-Key Lock)이란? **넥스트키 락**은 **레코드 락**과 **갭락**을 결합한 형태로, 특정 인덱스 레코드와 그 주변의 갭을 동시에 잠그는 락입니다. 이를 통해 레코드 자체의 변경과 함께 그 주변 공간의 변경도 동시에 제어할 수 있습니다. 넥스트키 락은 특정 레코드와 그 주변 공간을 잠그기 때문에, 다른 트랜잭션이 새로운 레코드를 삽입하여 팬텀 리드를 발생시키는 것을 방지합니다. | orders_id | orders_amount | | --------- | ------------- | | 1 | 100 | | 2 | 200 | | 3 | 300 | ```sql -- 트랜잭션 A 시작 START TRANSACTION; -- 트랜잭션 A amount = 200인 orders_id = 2 레코드에 대한 레코드 락과 1-2, 2-3에 대한 갭락을 동시에 잠금으로써 넥스트키 락을 설정 SELECT * FROM orders WHERE orders_amount = 200 FOR UPDATE; -- 트랜잭션 B 시작 START TRANSACTION; -- 트랜잭션 B orders_id = 4, orders_amount = 200인 레코드 삽입 시도 시, 넥스트키 락으로 인해 차단되어 대기 INSERT INTO orders (orders_id, order_amount) VALUES (4, 200); ... ``` ## 갭락과 넥스트키 락을 통한 팬텀 리드 방지 메커니즘 **트랜잭션 A**가 특정 범위의 데이터를 조회할 때, 해당 범위에 대해 갭락 또는 넥스트키 락을 설정합니다. 락이 설정된 범위 내에서는 **트랜잭션 B**가 새로운 레코드를 삽입하거나 기존 레코드를 수정하는 것이 차단됩니다. 따라서, 트랜잭션 A가 다시 동일한 조건으로 조회를 수행하더라도, 트랜잭션 B에 의해 새로운 데이터가 삽입되지 않아 팬텀 리드가 발생하지 않습니다. ## 추가 학습 자료를 제공합니다. - [MySQL - InnoDB Locking](https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html) - [MySQL - Transaction Isolation Levels](https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html) - [[MySQL] 스토리지 엔진 수준의 락의 종류(레코드 락, 갭 락, 넥스트 키 락, 자동 증가 락)](https://mangkyu.tistory.com/298)