Q1. 공유 락과 배타 락에 대해서 설명해주세요.
DBMS에서 트랜잭션을 특별한 제어 없이 병행 수행을 허용한다면 데이터의 일관성과 무결성을 보장하기 어려울 수 있습니다. 이때, 병행 수행되는 트랜잭션들을 제어하기 위해서 락을 사용할 수 있으며 DBMS에서 락은 크게 공유 락과 배타 락으로 분류할 수 있습니다.
공유 락(Shared Lock) 은 읽기 락(Read Lock)이라고 부르며, 공유 락이 걸린 데이터에 대해서 다른 트랜잭션에서도 공유 락을 획득할 수 있지만, 배타 락은 획득할 수 없습니다. 즉, 공유 락을 사용하면 트랜잭션 내에서 조회한 데이터가 변경되지 않는다는 것을 보장합니다.
배타 락(Exclusive Lock) 은 쓰기 락(Write Lock)이라고 부르며, 배타 락이 걸린 데이터에 대해서 다른 트랜잭션에서는 공유 락과 배타 락을 획득할 수 없습니다. 즉, 배타 락을 획득한 트랜잭션은 데이터에 대한 독점권을 가집니다.
Q2. 데드 락은 언제 발생하며 어떻게 해결할 수 있나요?
데드 락(Dead Lock) 이란 교착 상태로, 두개 이상의 트랜잭션이 서로 필요로 하는 데이터의 락을 점유하고 있어서 무한히 대기하는 상황을 말합니다. 트랜잭션은 락을 획득하지 못하는 경우, 다른 트랜잭션이 점유하고 있는 락이 해제될 때까지 대기합니다. 이런 상황을 해결하기 위해 락 획득 순서를 고정하거나 타임아웃을 설정할 수 있습니다.
Q3. 데이터베이스 커넥션 풀(Connection Pool)을 사용하지 않으면 어떤 문제가 발생할 수 있나요?
커넥션 풀이 없다면 애플리케이션에서 데이터베이스에 접근해야하는 요청을 처리할 때마다 커넥션을 새로 생성하여 연결하고 해제하는 과정을 반복해야 합니다. 이 과정은 비용이 상당히 많이 들기 때문에 요청의 응답시간이 길어집니다.
또 동시에 많은 요청이 들어올 경우 매번 새로운 커넥션을 생성하게 되는데, 데이터베이스의 최대 연결 수를 초과할 수 있습니다. 데이터베이스는 일반적으로 동시에 처리할 수 있는 요청 개수에 제한이 있는데, 이 제한을 초과하면 요청이 거부되어 사라지거나, 데이터베이스 자체가 비정상 종료될 수 있습니다.
Q4. 데이터베이스 커넥션 풀을 사용함으로써 얻을 수 있는 장점은 무엇인가요?
커넥션 풀(Connection Pool)은 애플리케이션과 데이터베이스 간의 데이터베이스 연결(Connection)을 미리 생성해두고, 이를 재사용하는 기법을 말합니다. 데이터베이스에 접근할 때마다 새로운 연결을 생성하고 종료하는 대신, 미리 준비된 연결을 재사용함으로써 성능을 향상시키고 자원 사용을 최적화할 수 있습니다.
Q5. 커넥션 풀 사이즈는 클 수록 좋나요?
커넥션을 사용하는 주체는 스레드(Thread)이기 때문에, 커넥션과 스레드를 연결지어 생각해야 합니다. 만약 커넥션 풀 사이즈가 스레드 풀 사이즈보다 크면, 스레드가 모두 사용하지 못해서 리소스가 낭비됩니다. 반대로 커넥션 풀 사이즈가 스레드 풀 사이즈보다 작으면, 스레드가 커넥션이 반환되기를 기다려야 하기 때문에 작업이 지연됩니다.
커넥션 풀 사이즈와 스레드 풀 사이즈의 균형이 맞더라도, 너무 큰 사이즈로 설정하면, 데이터베이스 서버, 애플리케이션 서버의 메모리와 CPU를 과도하게 사용하게 되므로 성능이 저하됩니다.
Q6. 트랜잭션 격리수준은 무엇인가요?
트랜잭션의 격리 수준은 동시에 여러 트랜잭션이 실행될 때 한 트랜잭션이 다른 트랜잭션의 연산에 영향을 받지 않도록 하는 정도를 말합니다.
낮은 격리 수준은 동시 처리 능력을 높이지만, 데이터의 일관성 문제를 발생시킬 수 있습니다. 반면, 높은 격리 수준은 데이터의 일관성을 보장하지만, 동시 처리 능력이 떨어질 수 있습니다. 즉, 데이터 정합성과 성능은 트레이드 오프 관계입니다. 트랜잭션 격리 수준은 개발자가 트랜잭션 격리 수준을 설정할 수 있는 기능을 제공하는 기능입니다.
Q7. 트랜잭션 격리 수준은 어떤 것이 있고 각각 어떤 특징이 있나요?
트랜잭션 격리 수준은 READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ가 존재합니다.
- READ UNCOMMITTED: 커밋이 되지 않은 트랜잭션의 데이터 변경 내용을 다른 트랜잭션이 조회하는 것을 허용합니다. 하지만 해당 격리 수준에서는 Dirty Read, Phantom Read, Non-Repeatable Read 문제가 발생할 수 있습니다.
- READ COMMITED: 커밋이 완료된 트랜잭션의 변경사항만 다른 트랜잭션에서 조회할 수 있도록 허용합니다. 특정 트랜잭션이 이루어지는 동안, 다른 트랜잭션은 해당 데이터에 접근할 수 없습니다. Dirty Read는 발생하지 않지만, Phantom Read, Non-Repeatable Read 문제가 발생할 수 있습니다.
- REPEATABLE READ: 한 트랜잭션에서 특정 레코드를 조회할 때 항상 같은 데이터를 응답하는 것을 보장합니다. 하지만, SERIALIZABLE과 다르게 행이 추가되는 것을 막지는 않습니다. 따라서 Phantom Read 문제가 발생할 수 있습니다.
- SERIALIZABLE: 특정 트랜잭션이 사용중인 테이블의 모든 행을 다른 트랜잭션이 접근할 수 없도록 잠급니다. 가장 높은 데이터 정합성을 가지지만 그 만큼 고립 수준이 높기 때문에 성능이 가장 낮습니다. MySQL의 경우 단순한 SELECT 쿼리가 실행되더라고 데이터베이스 잠금이 걸려 다른 트랜잭션에서 데이터에 접근할 수 없습니다.
Q8. 데이터베이스 인덱스에 대해서 설명해주세요.
인덱스는 데이터베이스 테이블의 검색 속도를 향상시키기 위한 자료구조로 백과사전의 색인과 같습니다. 저장되는 컬럼의 값을 사용하여 항상 정렬된 상태를 유지하는 것이 특징입니다. 하지만, 이러한 특징으로 인해 인덱스는 INSERT,UPDATE,DELETE의 성능이 희생된다는 단점이 있습니다.
MySQL InnoDB를 기준으로는 B+Tree와 같은 변형 B-Tree 자료구조를 이용해서 인덱스를 구현합니다. B-Tree 인덱스는 컬럼의 값을 변형하지 않고 인덱스 구조체 내에서 항상 정렬된 상태로 유지합니다.
Q9. 분산 환경에서 Redis를 활용한 잠금은 어떻게 구현할 수 있나요?
Redis의 SET 명령어를 사용해서 분산 잠금을 구현할 수 있습니다. 가령, 1대 이상의 서버가 특정 Key에 대해서 SET 명령어에 NX 옵션을 추가하여 Redis에 전달하여 잠금 획득을 시도합니다. NX 옵션을 사용하면 특정 Key에 해당하는 값이 존재하지 않는 경우에만 값 추가 작업이 성공합니다. SET 작업을 성공적으로 수행한 서버는 잠금을 획득합니다. 잠금을 획득한 서버는 작업이 끝난 이후, Key에 해당하는 값을 제거하여 잠금을 해제합니다.
잠금이 유실될 가능성이 있지 않나요?
만약, 레플리케이션 구성으로 이루어져 있으면 잠금이 유실될 가능성이 존재합니다. 예를 들어, 마스터 노드가 존재하고 레플리카 노드에 주기적으로 데이터를 복제하는 구성이라고 가정하겠습니다. 이때, 특정 서버가 잠금을 획득하고 작업을 수행하는 도중에 마스터 노드에 장애가 발생하는 경우에는 레플리카 노드가 마스터 노드로 승격될 수 있습니다. (failover)
위와 같은 가정에서 기존 마스터 노드가 가지고 있던 잠금용 데이터를 레플리카 노드가 가지고 있지 않은 상태라면, 작업을 처리하고 있는 서버의 잠금이 유실됩니다. 즉, 복제 지연으로 인해 분산 잠금의 상호 배제라는 특성을 잃게 됩니다. 이러한 문제를 해결하기 위해서 RedLock 알고리즘이 등장했습니다.
RedLock 알고리즘은 어떻게 동작하나요?
RedLock 알고리즘은 1대 이상의 단일 레디스 노드들을 이용하여 분산 잠금을 구현하는데요. 과반수 이상의 노드에 잠금을 획득한다면 잠금을 획득하는 것으로 간주합니다. 만약, 5개의 단일 레디스 노드가 존재한다고 가정하겠습니다. 작업 서버가 모든 노드에게 잠금 획득을 시도했는데, 3개(과반수) 노드에서 잠금을 획득다면, 분산 잠금을 획득한 것으로 판단합니다.
Q10. 데이터베이스 정규화에 대해서 설명해주세요.
데이터베이스에서 정규화(Normalization) 는 테이블을 정리하여 중복 데이터를 최소화하고, 데이터 무결성을 보장하는 과정을 의미합니다. 이를 통해 데이터 저장 용량을 줄이고, 삽입·갱신·삭제 이상(Anomaly) 현상을 해결할 수 있습니다.
각 정규화 단계를 설명해주세요.
- 1 정규화(1NF) 는 테이블 컬럼의 값이 원자값(Atomic Value)을 가지도록 정리하는 것을 의미합니다.
- 2 정규화(2NF) 는 1 정규화를 진행한 테이블에서 완전 함수 종속을 만족할 수 있도록 테이블을 분해하는 것인데요. 쉽게 표현하자면, 기본 키의 일부에만 종속된 속성이 없도록 분해하는 것을 의미합니다.
- 3 정규화(3NF) 는 2 정규화를 진행한 테이블에서 이행적 종속을 제거하기 위해 테이블을 분해하는 것을 의미합니다. A가 B를 결정하고, B가 C를 결정하는 경우에는 A가 C의 결정자가 되는데요. 이를 이행적 종속이라고 합니다. 3NF을 만족하기 위해서는 이행적 종속이 제거되도록 테이블을 분리해야 합니다.
- BCNF 정규화 는 3 정규화를 진행한 테이블의 모든 결정자가 반드시 후보키가 될 수 있도록 테이블을 분해하는 것을 의미합니다. BCNF를 만족하기 위해서는 후보키가 아닌 결정자가 존재하지 않도록 테이블을 분리해야 합니다.
이 페이지는 면접 준비를 위한 정리 페이지입니다.