Home [CS 스터디] 데이터베이스 1
Post
Cancel

[CS 스터디] 데이터베이스 1

1. Key (기본키, 후보키, 슈퍼키 등등…) 에 대해 설명해 주세요.

  • 키는 하나의 테이블에서 고유한 값을 가지며 데이터를 구분하기 위해 사용되는 필드를 의미합니다.
  • 키를 통해 DB에 저장된 레코드에 대한 식별하고 관계를 유지할 수 있습니다.
  • 키의 종류는 다음과 같습니다.
    • 기본키
      • 하나의 테이블에서 레코드마다 고유하게 지정되는 키를 의미합니다.
      • 기본키 값은 고유해야 하며 중복이 허용되지 않습니다.
    • 후보키
      • 기본키로 사용될 수 있지만 아직 그렇지 않은 키들을 의미합니다.
      • 유일성과 최소성(최소한의 필드로 유일성을 보장하는 것)이 보장된 형태여야 합니다.
    • 슈퍼키
      • 레코드를 고유하게 식별하기 위해 사용되는 필드의 조합을 의미합니다.
      • 중복된 값이 없도록 구성해야 해 유일성은 지키지만 최소성은 지키지 못합니다.
    • 대체키
      • 후보키 중에서 기본키로 선택되지 못한 키를 의미합니다.
      • 기본키를 제외한 다른 후보키로서 유일성을 가집니다.

기본키는 수정이 가능한가요?

  • 기본적으로 불가능해야 합니다.
  • 수정을 통해 유일성이 깨질 수 있는 위험이 있으며 기본키를 통해 관계를 유지하는 상황 속에서 혼란을 야기할 수 있습니다.
  • 수정을 해야 한다면 해당 기본키를 가지고 조회가 가능한 모든 테이블에서 수정이 이뤄져야만 합니다.

사실 MySQL의 경우, 기본키를 설정하지 않아도 테이블이 만들어집니다. 어떻게 이게 가능한 걸까요?

  • MySQL은 내부적으로 PK가 없더라도 Auto-Increment 방식의 칼럼으로 PK를 지정한다고 알고 있습니다.

외래키 값은 NULL이 들어올 수 있나요?

  • 들어올 수 있습니다.
  • 외래키는 두 테이블 간의 관계를 PK값을 통해 표현하는데 만약 이 외래키가 없다면 해당 레코드는 관계를 가지지 않는다고 이해할 수 있습니다.
  • 다만 NULL이 허용되는 만큼 이에 대한 철저한 체크가 보장되게끔 로직이 구현되어야 합니다.

어떤 칼럼의 정의에 UNIQUE 키워드가 붙는다고 가정해 봅시다. 이 칼럼을 활용한 쿼리의 성능은 그렇지 않은 것과 비교해서 어떻게 다를까요?

  • 추가, 수정 : 중복에 대한 검증이 필요하므로 추가적인 작업이 동작해야 합니다.
  • 조회 : 추가적인 검증이 없으므로 크게 다르지 않습니다. 만약 인덱스로 해당 칼럼을 사용한다면 조회 시 더 빠른 성능을 가져올 수 있습니다.
  • 삭제 : 다른 점이 없을 것 같습니다. 오히려 중복된 레코드를 삭제하지 않는 다는 사실을 알 수 있습니다.

2. RDB와 NoSQL의 차이에 대해 설명해 주세요.

참고 자료

  • 데이터 모델
    • RDB : 테이블 형태로 고정되어 있어 정형 데이터로 제공됩니다.
    • NoSQL : 키-값, 도큐먼트, 그래프 등 다양한 형태의 비정형 데이터를 제공합니다.
  • 확장
    • RDB : 좋은 서버를 하나 더 두거나 서버의 스펙을 높이는 수직적 확장에 유리합니다.
    • NoSQL : 서버의 개수를 늘려 분산 처리가 가능한 수평적 확장에 유리합니다.
  • 스키마
    • RDB : 스키마가 고정적이고 변경하기가 어렵습니다.
    • NoSQL : 스키마 고정적이지 않고 유연합니다.
  • 그 외에도 관계형 데이터로 저장 여부, Join 및 쿼리문 언어 존재 여부, 트랜잭션 사용 여부 등에서도 차이를 들어냅니다.

NoSQL에 트랜잭션이 없는 이유

  • 확장성을 위한 목적을 가지는 NoSQL에선 복잡한 트랜잭션은 노드간의 일관성을 유지하는데 어렵게 만듭니다.
  • 데이터 모델의 다양성으로 인해 트랜잭션 구현이 어렵습니다.
  • 응답 속도 측면에서 장점을 가져야 하는 NoSQL에서 트랜잭션은 불필요한 기능으로 볼 수 있습니다.

NoSQL의 강점과, 약점이 무엇인가요?

  • 강점
    • 동적인 형태로 데이터를 관리할 수 있어 변화에 유연하게 대처가 가능합니다.
    • 동적으로 서버 확장을 이뤄낼 수 있습니다.
    • 대용량 데이터를 빠른 속도를 통해 처리할 수 있습니다.
  • 단점
    • 유연한 만큼 데이터의 일치성을 보장을 하지 못합니다.
    • 복잡한 관계를 가진 데이터를 관리하기 어렵습니다.
    • 중복이 발생할 수 있습니다.

RDB의 어떠한 특징 때문에 NoSQL에 비해 부하가 많이 걸릴 수 있을까요?

  • 데이터 간의 관계성을 가지고 이것이 용량이 커질수록 복잡도가 높아지기 때문입니다.
  • 이 관계성을 위해 Join 연산을 통해 데이터 간의 관계를 이어줘야 하고 그 관계에 대한 데이터를 수정, 조회하는 것은 단순한 형태로 저장하는 NoSQL에 비해 느릴 수 있습니다.
  • 또한 트랜잭션 단위로 연산이 처리되기에 트랜잭션 간의 원활한 데이터 처리 및 안정성을 위한 동시성 제어도 필요합니다.

NoSQL을 활용한 경험이 있나요? 있다면, 왜 RDB를 선택하지 않고 해당 DB를 선택했는지 설명해 주세요.

  • 로그인 인증 과정 중 세션 저장소로 Redis를 활용한 적이 있습니다.
    • 인메모리 방식을 통한 빠른 작업 속도와 간단한 키-값 형태의 데이터 유지만을 위해 사용할 목적으로 사용했기 때문입니다.
    • 데이터가 한번 저장되면 삭제되기 전까지 수정에 대한 작업은 거의 없다고 판단할 수 있어 데이터의 일치성과 정확도에 대한 고려를 상대적으로 덜 해도 된다고 판단했습니다.
  • 추가적으로 채팅서버 기능 구현 시에도 사용
    • 빠른 처리 속도는 물론 SortedSet과 같은 다양한 형태의 자료구조를 통해 필요한 상황에 맞게 알맞은 자료구조를 사용하기 위해서입니다.

3. 트랜잭션이 무엇이고, ACID 원칙에 대해 설명해 주세요.

NoSQL의 경우 BASE -> 결국 NoSQL 궁극적인 일관성을 보장하려고 한다.

  • 트랜잭션은 작업의 완전성을 보장하는 것을 의미합니다.
  • 논리적인 작업 셋이 정상적으로 모두 처리가 완료되었다면 그 때 커밋을 통해 데이터 변경을 이뤄지도록 하거나 중간에 하나의 쿼리라도 에러가 발생했다면 작업 시작 이전의 상태로 원복(롤백)시키는 것이 트랜잭션의 역할입니다.
  • 즉, 논리적인 작업 셋의 내용이 완전히 적용되거나 그렇지 않다면 아예 적용되지 않은 상태로 유지하는 것을 보장하는 것(정합성을 보장하는 것)이 트랜잭션입니다.
  • ACID 원칙은 트랜잭션의 완전성을 보장하기 위해 지켜져야 할 네가지 특성을 의미합니다.(참고자료)
    • 원자성(Atomicity)
      • 시스템에서 원자성이란 중간 결과가 없이 작업 전, 후만 알 수 있는 성질을 의미합니다.
      • 이를 트랜잭션에 대입해본다면, 트랜잭션의 결과는 커밋과 롤백만 존재해야 한다고 이해할 수 있습니다.
    • 일치성(Consistency)
      • 트랜잭션 전, 후 모두 데이터베이스의 제약과 규칙에 만족해야 한다는 성질입니다.
      • 즉, null이 허용되지 않는 컬럼에 대해서는 트랜잭션 전, 후 모두 이 조건에 부합하도록 유지되어야 한다는 것입니다.
      • 이를 보장하는 건 데이터베이스에선 제한적(외래키, 유일성 보장 등의 간단한 제약조건문)이며, 어플리케이션 단에서 이를 지키기 위한 노력이 필요합니다.
    • 격리성(Isolation)
      • 한마디로 트랜잭션 간의 작업에 있어 영향을 줘서는 안된다는 의미입니다.
      • 여러 트랜잭션의 하나의 테이블에 접근할 때, 동시성 문제가 발생할 수 있어 격리성을 지키기 위한 격리 수준을 정해줘야 합니다.
    • 지속성(Durablity)
      • 커밋된 이후에 데이터베이스에 저장되었다면, 어떠한 상황에서도 데이터는 유지되어야 하는 성질입니다.
      • 즉, 하드웨어적 손상이나 데이터베이스의 손상이 발생해도 저장된 데이터는 보존되어야 한다는 의미입니다.
      • 절대적인 지속성의 보장은 없습니다. 데이터베이스가 관리되는 서버가 화재로 인해 타버린다면?

ACID 원칙 중, Durability를 DBMS는 어떻게 보장하나요?

  • 트랜잭션 로그
    • 트랜잭션 내용을 별도의 로그로 저장해 관리하는 방식을 의미합니다.
    • 데이터 커밋 전에 트랜잭션 로그를 우선적으로 저장하는 걸 WAL(Write-Ahead Logging) 방식이라고 합니다.
  • Replication
    • 데이터 베이스 서버를 복제본을 두고 동기화 함으로써 하나의 서버가 다운되어 데이터의 손상이 날 수 있는 경우를 대비할 수 있습니다.
  • Redo 로그
    • MySQL의 경우, 모든 데이터를 조회해오기 위해 디스크에 직접 접근한다면 성능 상으로도 안좋을 뿐더러 디스크에도 매우 큰 부하를 줄 수 있습니다.
    • 그래서 데이터의 변경 내용을 바로 디스크에 저장하는 것이 아닌 버퍼풀이라는 메모리에 우선적으로 저장해두고 사용하는 방식을 선택하고 있습니다.
    • 버퍼풀의 주 목적은 캐싱을 통한 조회 속도 향상과 변경 내용을 쓰기 지연하는 것에 있습니다.
    • 하지만 버퍼 풀의 경우에도 하드웨어적 문제가 발생하면 데이터 손상에서 자유로울 수 없습니다.
    • 그래서 데이터의 변경이 발생할 때, 버퍼풀에 변경된 데이터를 저장하면서 변경 내용을 별도로 저장해두는 이걸 Redo 로그라고 합니다.
    • Redo 로그를 사용하면, 버퍼 풀에 적용된 변경 데이터들의 변경 시점과 내용을 확인 할 수 있어 복구에 사용될 수 있습니다.

언두(Undo) 로그란? 리두 로그가 커밋되기 전인 변경 사항을 저장한 것이라면 언두 로그는 변경된 이후 변경 전 정보를 가진 로그를 의미합니다.

트랜잭션을 사용해 본 경험이 있나요? 어떤 경우에 사용할 수 있나요?

  • 여러 테이블에 대한 변경 소요가 발생하는 비즈나스 로직 단위로 트랜잭션을 거는 경우가 있다고 생각합니다.
  • 결제의 경우, 결제자의 통장 잔고 변화, 실제 결제를 위한 거증 및 절차 진행, 판매자 통장 잔고 변화 등 여러 변경 사항이 하나의 작업 단위로 묶여야 하는 경우에 트랜잭션을 사용해 더 안전한 작업 진행이 가능하도록 할 수 있습니다.

읽기에는 트랜잭션을 걸지 않아도 될까요?

  • 읽기의 경우, 다른 추가, 수정, 삭제 작업에 비해 트랜잭션을 걸지 않아도 문제가 되지 않는 경우가 더 많다고 생각합니다.
  • 하지만 읽기만을 위한 작업을 하는 중에도 다른 트랜잭션으로 인해 데이터가 수정되어 일치하지 않는 경우가 있을 수 있습니다.
  • 또한 데이터의 무결성을 위해 읽기 작업 중 이상을 감지하고 트랜잭션을 통해 롤백을 진행해야 하는 경우도 있을 수 있습니다.
  • 빠른 성능을 요구하고 데이터의 정합성이나 무결성에 대해선 상대적으로 나슨한 규제를 걸어도 되는 상황이라면 트랜잭션을 제외하는 것이 합리적일 수 있지만
  • 조회임에도 불구하고 정확한 데이터의 유지를 요구하다면 트랜잭션을 거는 것이 좋다고 생각합니다.

그렇다면 Spring에서 Transactional(readOnly = true) 어노테이션은 왜 거는 걸까?

  • 참고자료
  • 코드 가독성
  • JPA의 쓰기지연, 플러시 동작을 하지 않는다고 종결내 변경 감지에 대한 스냅샷 불필요
  • Replication 환경에서 Slave DB로만 접근하도록 구성 가능

4. 트랜잭션 격리 레벨에 대해 설명해 주세요.

  • 트랜잭션의 격리 수준, 격리 레벨이란 트랜잭션 간에 공개한 작업 내용의 범위, 수준을 의미합니다.
  • 대표적인 격리 레벨은 아래와 같으며 가장 규제가 약한 순으로 설명합니다.
    1. READ-UNCOMMITED
      • 아직 커밋되지 않은 데이터도 다른 트랜잭션과 공유합니다.
      • 이로 인해 Dirty Read가 발생합니다.(Dirty-Read : 실제로 커밋되지 않은 데이터를 읽는 경우)
    2. READ-COMMITED
      • 커밋된 데이터에 대해서만 공유합니다.
      • Dirty Read는 해결되지만 NON-REPEATABLE READ 문제가 발생합니다.(NON-REPEATABLE READ : 하나의 트랜잭션에서 같은 조회 쿼리를 통해 얻는 데이터가 다른 경우)
      • 이 격리 레벨을 위해 언두 로그를 사용합니다.
      • ORACLE 기본 격리 수준
    3. REPEATABLE-READ
      • 하나의 트랜잭션 내에서 같은 조회 쿼리문에 대해서 조회 결과가 다른 트랜잭션의 작업 내용과 상관없이 동일하도록 보장하는 격리 수준을 의미합니다.
      • 트랜잭션의 레벨을 부여해 해당 트랜잭션보다 낮은 레벨을 가진 트랜잭션이 관여한 수정 사항에 대해선 접근하지 않도록 구현되어 있습니다.(이를 MVCC 방식이라고 합니다.)
      • 이를 통해 NON-REPEATABLE READ는 해결했지만 추가 작업으로 인해 데이터가 없다가 생겨지는 PHANTOM-READ 문제가 발생할 수 있습니다.
      • MySQL 기본 격리 수준
    4. SERIALIZABLE
      • 하나의 트랜잭션이 하나의 테이블에만 접근할 수 있고 그 외 트랜잭션의 접근과 수정은 제한하는 격리 레벨입니다.
      • 위에서 발생하는 문제는 발생하지 않지만 그만큼 성능은 저하시킵니다.

사실 MySQL에선 넥스트 키 락과 갭락으로 REPEPEATABLE READ에서도 PHANTOM-READ가 발생하지 않습니다. (해당 트랜잭션이 접근한 레코드 주변에 대한 락을 걸기 때문에 범위에 속하는 데이터에 대한 다른 트랜잭션의 작업이 진행되지 않습니다.)

MVCC 방식

  • 트랜잭션마다 레벨을 부여해 시작 시점을 표현합니다.
  • 이 레벨을 통해 현재 트랜잭션보다 이후 시점에 시작한 트랜잭션이 변경한 데이터는 접근하지 않고 Undo 로그에서 접근 가능한 데이터롤 읽습니다.

모든 DBMS가 4개의 레벨을 모두 구현하고 있나요? 그렇지 않다면 그 이유는 무엇일까요?

  • 모두 구현하고 있지 않습니다.
  • 이는 DBMS마다 상이한 동작 방식과 내부 구현 방식에 따라 달라질 수 있습니다.
  • 예를 들어 ORACLE의 경우, 동시성과 격리성을 중요하게 생각하고 동시성에 대해선 성능 저하를 우려해 일부 격리 수준만 제공합니다.

만약 MySQL을 사용하고 있다면, (InnoDB 기준) Undo 영역과 Redo 영역에 대해 설명해 주세요.

  • Redo 영역은 버퍼 풀에 저장되어 아직 실제 디스크에 적용 전인 내용들에 대한 변경 사항을 저장한 영역을 의미합니다.
  • Undo 영역은 커밋 이후 실제로 디스크에 저장된 데이터 이전에 저장된 변경 이전 데이터를 저장한 영역입니다.

그래서 데이터 복구 시 Undo 영역 데이터를 우선 복구한 다음, Redo 영역 데이터를 활용해 변경 사항들을 다시 적용합니다.

그런데, 스토리지 엔진이 정확히 무엇을 하는 건가요?

  • 요청으로 넘어온 쿼리를 분석하고 처리해 실제 물리적인 데이터를 가공하는 두뇌 역할을 하는 것을 스토리지 엔진이라고 합니다.
  • 스토리지 엔진은 하나의 DB에서도 여러 종류를 지원합니다. 테이블마다 다르게 스토리지 엔진을 활용할 수 있습니다.
  • 각각의 스토리지 엔진은 물리적인 데이터 저장 및 접근 방식을 다르게 정의하고 있습니다.
    • MyISAM : 단순한 저장 구조로 속도는 빠르지만 동시성 제어에 대해선 제공되는 기능이 적습니다.
      • 트랜잭션 미지원으로 ACID 보장을 하지 못합니다.
      • 테이블 단위 락만 지원
    • InnoDB : 데이터 로드 속도는 상대적으로 느리지만 동시성 제어를 위한 기능이 더 많이 제공되며, 멀티 스레드 환경에서 높은 효과를 가져옵니다.
      • 트랜잭션 지원으로 ACID 보장
      • 레코드 단위 락 지원

5. 인덱스가 무엇이고, 언제 사용하는지 설명해 주세요.

  • 인덱스란, 데이터의 빠른 조회를 위해 데이터를 정렬해서 저장하기 위해 사용되는 기준값을 의미합니다.
  • 인덱스는 데이터의 저장, 수정보다 조회시 장점을 가져올 수 있어 빠른 조회를 요구하는 상황에서 사용됩니다.
  • MySQL의 경우, PK값이 인덱스로 자동 지정됩니다.

일반적으로 인덱스는 수정이 잦은 테이블에선 사용하지 않기를 권합니다. 왜 그럴까요?

  • 인덱스를 사용하게 되면 새로 저장될 데이터를 기존에 정렬된 데이터에 맞춰 저장할 위치를 정해야하고 새롭게 재정렬을 해줘야 하기 때문입니다.
  • 단순하게 저장하는 것이 아닌 정렬을 통한 저장이 이뤄져야 해서 추가적인 작업 소요가 발생합니다.

앞 꼬리질문에 대해, 그렇다면 인덱스에서 사용하지 않겠다고 선택한 값은 위 정책을 그대로 따라가나요?

인덱스에서 사용하지 않겠다 -> 삭제로 인해 인덱스에서 제외된 값?

  • 레코드를 저장할 때, 인덱스를 사용하지 않는다면 물리적으로 메모리상 정렬을 하지 않습니다.
  • 이로 인해 조회 시, 인덱스를 사용할 때보다 성능이 저하될 수 있습니다.
  • 반대로 수정 작업에 대해선 정렬에 대한 고려나 추가 작업이 이뤄지지 않으므로 더 나은 성능을 가져올 수 있습니다.

ORDER BY/GROUP BY 연산의 동작 과정을 인덱스의 존재여부와 연관지어서 설명해 주세요.

  • 인덱스가 존재하고, 인덱스를 통해 ORDER BY / GROUP BY를 하는 경우, 이미 정렬된 상태이기에 더 빠른 정렬이 가능합니다.
  • 또한 데이터의 그룹을 지정할 때에도 정렬된 메모리의 위치를 빠르게 조회할 수 있어 성능이 향상될 수 있습니다.
  • 하지만 인덱스가 없다면, 전체 데이터를 조회해 정렬 후 조건에 맞는 데이터를 추려내야 하기 때문에 추가작업으로 인해 성능이 저하됩니다.

기본키는 인덱스라고 할 수 있을까요? 그렇지 않다면, 인덱스와 기본키는 어떤 차이가 있나요?

  • 완전히 같은 개념이라고 볼 수는 없습니다. 기본키는 인덱스의 한 종류이지만 다른 일반 컬럼값도 인덱스로 지정해 활용할 수 있습니다.
  • 하지만 기본키는 고유한 식별값으로 사용되고 관리되므로 인덱스로 사용하기에 매우 적합합니다.
  • 그래서 MySQL은 인덱스를 별도로 지정하지 않더라도 PK를 기본 인덱스로 지정합니다.
  • 기본키는 레코드를 식별하고 다른 테이블과의 관계를 맺기 위해 필요한 값이라면, 인덱스는 하나의 테이블에 저장된 레코드들의 순서를 정하고 정렬된 형태로 저장하기 위한 색인으로 이해할 수 있습니다.

그렇다면 외래키는요?

  • 외래키는 다른 두 테이블을 연결하기 위해 사용되는 중간 다리 값으로 사용됩니다.
  • 외래키 또한 인덱스로 사용할 수 있습니다.
  • 그러나, JOIN 연산을 통해 관계를 가진 연관 객체로 조회가 많이 되는 경우라면, 외래키를 인덱스 중 하나로 함께 등록해 더 빠른 조회 성능을 가져올 수 있습니다.

만약 NULL 값이나 중복값으로 인덱스의 활용이 제한되는 경우라면? 비정규화를 고민해보는 것이 좋지 않을까하는 생각이 듭니다.

6. RDBMS, NoSQL에서의 클러스터링/레플리케이션 방식에 대해 설명해 주세요.

추가 키워드 : fail-over, Peer To Peer

  • 클러스터링
    • 여러 대의 DB서버 혹은 노드를 수평적으로 확장해 데이터를 관리하는 방식을 의미합니다.
    • 클러스터링은 Active-Active 방식과 Active-StandBy 방식으로 나뉨니다.
    • Active-Active 방식을 사용하면 데이터를 분산 처리하는 점에서 각각의 서버의 부하를 줄일 수 있지만 병목현상이 발생할 수 있습니다.
    • 그래서 병목 현상이 발생해 성능에 영향을 주는 경우, Active-StandBy 방식을 고려해볼 수 있습니다.
    • 하지만 Active-StandBy 방식 또한 DB가 교체되는 과정에서 일정 시간동안 서비스를 사용하지 못한다는 단점이 있습니다.
  • Replication
    • 같은 데이터를 가지는 복제 DB를 둠으로써 데이터의 유실에 대비하는 방식을 의미합니다.
    • 추가로 Master / Slave 형식으로 DB의 역할을 나눠 상대적으로 비율이 높은 조회에 대한 처리를 Slave에서 담당하도록 구성할 수 있습니다.
    • Replication을 통해 데이터의 데이터 복구에 대한 보장을 가져올 수 있으면서 역할의 분담으로 부하를 줄이는 효과도 있습니다.
    • 하지만 Master에서 수정된 내용을 Slave에 동기화하는 과정에서 발생하는 불일치성의 문제가 존재합니다.

이러한 분산 환경에선, 트랜잭션을 어떻게 관리할 수 있을까요?

하나 더 물어보기, 해결 방법 1 : 행 레벨 락

  • 2단계 커밋
    • 여러 분산 노드에게 커밋 준비에 대한 응답을 받고(1단계), 커밋 완료 응답을 받으면서(2단계) 전체 노드에 대한 원자성을 보장하는 방식을 의미합니다.
    • 하지만, 이 과정을 처리하는 관리자(코디네이터)에 문제가 생기면 트랜잭션이 보장되지 못합니다.(SPOF)
    • 그리고 처리 과정에서 네트워크 에러나 하드웨어적 에러가 발생하면 ACID 보장이 어렵습니다.

이래서 분산 환경에 대한 목적으로 탄생한 NoSQL에서 트랜잭션을 지원하지 않는 것이 아닌가 싶습니다. 트랜잭션을 위해 과한 설정이 필요하고 이로 인해 오히려 빠른 처리가 보장되지 않습니다. 트랜잭션의 장점을 모두 보장하기 위해선 속도보단 데이터의 안정성을 목적으로 두는 RDBMS가 어울립니다. 하지만 RDBMS에선 수평적 확장에 대해서는 한계가 있어 Replication을 채택하는 것이 아닐까 하는 개인적인 생각입니다.

결론은! 클러스터링에서 트랜잭션은 보장이 힘들다. 그래서 NoSQL은 트랜잭션을 거의 지원하지 않는다. 그리고 RDBMS는 트랜잭션에 대한 보장을 해줘야 하므로 클러스터링보단 복제에 대한 사용을 한다.

마스터, 슬레이브 데이터 동기화 전 까지의 데이터 정합성을 지키는 방법은 무엇이 있을까요?

  • 스냅샷 복제
    • 특정 시점의 마스터를 스냅샷을 찍어 슬레이브에 초기 데이터를 전달하는 방식입니다.
  • 트랜잭션을 이용한 데이터 복제
    • 마스터에서 데이터 변경이 발생할 때, 트랜잭션에 의한 변경이 이뤄질 수 있기에 트랜잭션 단위로 변경된 사항에 대해서 슬레이브에 복제해주는 방식입니다.
  • 바이너리 로그 활용
    • 마스터에서 발생한 변경 사항에 대한 정보가 담긴 파일을 바이너리 로그라고 합니다. 바이너리 로그가 생성되면 이를 슬레이브에서 읽어가서 데이터의 정합성을 지킬 수 있습니다.

반동기, 비동기 복제 참고 자료

다중 트랜잭션 상황에서의 Deadlock 상황과, 이를 해결하기 위한 방법에 대해 설명해 주세요.

  • 데드락이란 두개의 프로세스가 다른 데이터를 점유하고 있는 상태에서, 서로가 점유하고 있는 데이터를 요구하면서 무한히 대기하는 상태를 의미합니다.
  • 데드락이 발생하기 위해선 아래 조건을 모두 충족해야 합니다.
    • 상호 배제 : 프로세스는 한번에 하나의 자원만 점유가 가능하다.
    • 점유와 대기 : 이미 하나의 자원은 점유하고 있고 다른 자원을 대기하고 있는 프로세스가 있어야 한다.
    • 비선점 : 이미 점유된 자원을 뺏어올 수 없다.
    • 순환 대기 : 프로세스 집합에서 프로세스들이 자원에 대해서 순환 구조로 대기하고 있어야 한다.
  • 위와 같은 조건이 충족하는 상황에서 데드락 해결 방법은 아래와 같습니다.
    • 예방 : 데드락이 발생할 수 있는 조건을 미리 허용하지 않는 방식
      • 상호 배제 방지 : 모든 자원 공유 허용
      • 점유와 대기 방지 : 자원을 점유하지 않은 상태에서만 점유가 가능
      • 비선점 방지 : 필요한 자원을 미리 모두 선점
      • 순환 대기 방지 : 자원 요청을 순서에 따라 한방향으로 가능
    • 회피 : 데드락이 발생할 수 있는 상황을 피하는 방식으로 자원을 할당
      • 은행원 알고리즘 : 자원을 제공할 때, 모든 프로세스가 필요한 자원을 점유하고 데드락이 발생하지 않을 지 확인 후 점유를 허용하는 방식
    • 탐지 및 복구 : 데드락 발생 여부를 감지하고 문제가 해결될 때까지 복구하는 방식
      • 자원 할당 그래프로 자원과 프로세스간의 관계를 표현하고 탐지 알고리즘을 통해 순환 여부를 체크해 데드락 여부를 확인합니다.
      • 만약 데드락이 식별되었다면 모든 프로세스를 종료하거나 하나의 프로세스가 데드락이 해결될 때까지 자원을 뺐어 다른 프로세스에 할당합니다.

샤딩 방식은 무엇인가요? 만약 본인이 DB를 분산해서 관리해야 한다면, 레플리케이션 방식과 샤딩 방식 중 어떤 것을 사용할 것 같나요?

  • 샤딩이라는 것은 하나의 데이터 베이스를 여러 개의 샤드로 나눠서 각자가 중복되는 데이터 없이 독립적으로 저장 및 관리하는 방식을 의미합니다.
  • 즉, 샤드1과 샤드2에는 같은 테이블에 대한 데이터가 저장되더라도 특정 기준을 가지고 겹치는 레코드 없이 나눠서 저장하게 됩니다.
  • 샤딩을 사용하게 되면 데이터 복제 및 동기화에 대한 고려가 없고 온전히 분산 처리의 측면에서 과부화를 덜어주는 장점을 가져올 수 있습니다.
  • 하지만 특정 데이터에 대한 조회나 변경이 몰리거나 조인 연산 시 샤드 간의 데이터 저장 값이 다르다 보니 이로 인한 복잡도가 높아집니다.
  • 이에 반해 레플리케이션은 분산 처리보단 가용성과 안정성에 중점을 둔 방식입니다.
  • 이상적으로는 두 방식을 혼합해 각 샤드에 대한 복제본을 형성하는 것도 하나의 좋은 방법이 될 수 있을 것 같습니다.
  • 둘 중 하나를 선택해야 한다면
    • NoSQL를 사용해야 할 때에는 샤딩을 충분히 고려해볼 만하다고 생각합니다. 요청에 대한 복잡도가 낮기 때문입니다. 하지만 이 또한 안정성을 위해선 복제를 혼합해 사용하는 것이 합리적이라고 생각합니다.
    • 하지만 RDBMS의 경우는 트랜잭션과 조인 연산과 같이 레코드 간의 조합을 자주 요구하기 때문에 샤딩을 사용할 때 얻을 수 있는 이점보다 더 큰 작업 소요와 설계적 고민을 필요로 할 것 같습니다.
    • RDBMS를 사용하더라도 조인보단 특정 관계에 대한 조회가 반복적으로 요구되는 경우라면 샤딩으로 분산 처리를 할 수 있게 설계해볼 수는 있을 수 있다고 생각합니다.
    • 그러나 대부분의 경우 RDBMS에선 복제를 통해 안정성을 확보하고 Master/Slave의 역할 분담 정도로 가용성을 챙길 것 같습니다.

7. 정규화가 무엇인가요?

정규화란 데이터의 중복을 제거해 메모리의 효율적인 활용을 위해 테이블을 수정하기 위한 방식을 의미합니다.

정규화를 하지 않을 경우, 발생할 수 있는 이상현상에 대해 설명해 주세요.

참고자료

  • 삽입 이상 : 데이터를 추가하고자 할 때, 필요하지 않은 데이터까지 요구되는 경우가 발생합니다.
  • 삭제 이상 : 데이터를 삭제하고자 할 때, 요구사항과 관계없는 데이터까지 함께 삭제되는 경우가 발생합니다.
  • 갱신 이상 : 데이터를 수정하고자 할 때, 중복된 다른 레코드의 데이터들까지 함께 수정해줘야 하는 반복작업이 발생합니다.

각 정규화에 대해, 그 정규화가 진행되기 전/후의 테이블의 변화에 대해 설명해 주세요.

참고자료

  1. 제 1 정규형 : 테이블에 저장된 값은 원자값이여야 한다.
  2. 제 2 정규형 : 부분적 종속(기본키 중 일부에만 종속)을 제거한다. 즉, 완전 함수 종속에 만족해야 한다.
  3. 제 3 정규형 : 기본키를 제외한 속성간의 이행 종속성을 없앤다. A->B && B->C 일 때, A->C를 알 수 있다.
  4. BCNF 정규형 : 모든 결정자는 후보키 집합에 속해야 한다.
  5. 제 4 정규형 : 다치 종속을 제거한다. R(A, B, C)가 있을 때 A와 B 사이에 다치 종속성이 있을 때 B와 C가 독립적이다.
  6. 제 5 정규형 : 조인 종속을 제거한다. 두 테이블을 무손실 분해한 후 다시 조인해도 테이블이 유지된다면

정규화가 무조건 좋은가요? 그렇지 않다면, 어떤 상황에서 역정규화를 하는게 좋은지 설명해 주세요.

  • 무조건 좋다고 볼 수 없습니다.
  • 정규화를 통해 테이블을 작은 단위로 쪼개게 되면 여러 테이블간의 JOIN 연산이 증가하게 됩니다.
  • 이로 인해 복잡한 관계성이 생겨나고 쿼리문 처리 시 성능이 저하되는 경우가 발생할 수 있습니다.
  • 그래서 함께 자주 사용되는 데이터들에 대해서는 하나의 테이블에 묶어서 관리하는 역정규화를 고려하는 것이 필요합니다.
This post is licensed under CC BY 4.0 by the author.

[CS 스터디] 자료구조 2

[CS 스터디] 데이터베이스 2