이 글은 인프런 김영한님의 Spring 강의를 바탕으로 개인적인 정리를 위해 작성한 글입니다.
공유 락과 배타락 개념
데이터베이스 락은 동시성 제어를 위해 사용되는 기술이다. 이는 여러 사용자나 애플리케이션이 동시에 데이터베이스에 접근할 때 데이터의 일관성과 무결성을 유지하기 위해 필요하다. 데이터베이스 락에는 크게 두 가지 유형이 있다
공유 락(Shared Lock)
배타 락(Exclusive Lock)
공유 락(Shared Lock)
공유 락은 여러 트랜잭션이 동시에 데이터를 읽을 수 있도록 허용하지만, 해당 데이터에 대한 수정은 허용하지 않는다.이 락은 데이터의 동시 읽기를 가능하게 하여 성능을 향상시키지만, 동시에 데이터를 변경할 수는 없다.
배타 락(Exclusive Lock)
배타 락은 한 트랜잭션이 데이터에 대해 배타적인 접근 권한을 갖는다. 이는 해당 트랜잭션은 데이터를 읽고 수정할 수 있지만 배타 락이 설정된 동안 다른 어떤 트랜잭션도 해당 데이터를 읽거나 수정할 수 없다.
데이터베이스 락의 관리는 대부분의 데이터베이스 관리 시스템(DBMS)에서 자동으로 처리된다. 하지만 락이 잘못 관리되면 데드락이라고 하는 상황이 발생할 수 있다. 데드락은 두 개 이상의 트랜잭션이 서로의 락을 기다리며 무한정 대기하는 상황을 말한다. 따라서 데이터베이스 설계와 애플리케이션 개발 시 락의 사용 방법과 데드락 회피 기법을 이해하는 것이 중요하다.
데이터베이스 락을 효율적으로 관리하기 위한 몇 가지 전략은 다음과 같다.
락의 범위와 지속 시간 최소화: 필요한 최소한의 데이터에 대해서만 락을 걸고, 가능한 짧은 시간 동안만 락을 유지한다.
락 유형 선택: 데이터를 단순히 읽기만 할 경우 공유 락을, 데이터를 변경해야 할 경우에는 배타 락을 사용한다.
트랜잭션 분리: 큰 트랜잭션을 여러 작은 트랜잭션으로 분리하여 데드락의 가능성을 줄인다.
락 타임아웃 설정: 락을 얻기 위해 대기하는 최대 시간을 설정하여 데드락이 발생해도 시스템이 정지하지 않도록 한다.
공유 락과 배타 락 예시
세션1이 트랜잭션을 시작하고 데이터를 수정하는 동안 아직 커밋을 수행하지 않았는데, 세션2에서 동시에 같은 데이터를 수정하게 되면 여러가지 문제가 발생한다. 바로 트랜잭션의 원자성이 깨지는 것이다.
여기에 더해서 세션1이 중간에 롤백을 하게 되면 세션2는 잘못된 데이터를 수정하는 문제가 발생한다. 이런 문제를 방지하려면, 세션이 트랜잭션을 시작하고 데이터를 수정하는 동안에는 커밋이나 롤백 전까지 다른 세션에 서 해당 데이터를 수정할 수 없게 막아야 한다.(배타 락)
세션1은memberA의 금액을500원으로 변경하고 싶고,세션2는 같은memberA의 금액을1000원으로 변경하고 싶다. 데이터베이스는 이런 문제를 해결하기 위해 락(Lock)이라는 개념을 제공한다.
세션1은 트랜잭션을 시작한다. 세션1은 memberA 의 money 를 500으로 변경을 시도한다. 이때 해당 row의 락을 먼저 획득해야 한다. 락이 남아 있으므로 세션1은 락을 획득한다. (세션1이 세션2보다 조금 더 빨리 요청했다.) 세션1은 락을 획득했으므로 해당 row에 update sql을 수행한다.
세션2는 트랜잭션을 시작한다. 세션2도 memberA 의 money 데이터를 변경하려고 시도한다. 이때 우선 해당 로우의 락을 획득해야 한다. 락이 없으므로 락이 돌아올 때 까지 대기한다. 세션2가 락을 무한정 대기하는 것은 아니다. 락 대기 시간을 넘어가면 락 타임아웃 오류가 발생한다. 락 대기 시간은 설정할 수 있다.
이때 세션 2는 memberA 데이터 수정은 할 수 없지만 모든 속성을 조회할 수 있다(공유 락)
세션1은 커밋을 수행한다. 커밋으로 트랜잭션이 종료되었으므로 락도 반납한다.
락을 획득하기 위해 대기하던 세션2가 락을 획득한다.
세션2는 update sql을 수행한다.
세션2는 커밋을 수행하고 트랜잭션이 종료되었으므로 락을 반납한다.
데이터 조회와 락
일반적인 조회는 락을 사용하지 않는다 데이터베이스마다 다르지만, 보통 데이터를 조회할 때는 락을 획득하지 않고 바로 데이터를 조회할 수 있다. 예를 들어서 세션1이 락을 획득하고 데이터를 변경하고 있어도, 세션2에서 데이터를 조회는 할 수 있다. 물론 세션2에 서 조회가 아니라 데이터를 변경하려면 락이 필요하기 때문에 락이 돌아올 때 까지 대기해야 한다.
조회와 락
데이터를 조회할 때도 락을 획득하고 싶을 때가 있다. 이럴 때는 select for update 구문을 사용하면 된다. 이렇게 하면 세션1이 조회 시점에 락을 가져가버리기 때문에 다른 세션에서 해당 데이터를 변경할 수 없다. 물론 이 경우도 트랜잭션을 커밋하면 락을 반납한다.
SELECT FOR UPDATE 문은 데이터베이스에서 트랜잭션 처리 중에 특정 레코드에 대한 배타적 락을 설정하는 데 사용된다. 이 구문을 사용하면 트랜잭션이 해당 레코드를 읽고 업데이트할 수 있는 동안 다른 트랜잭션이 해당 레코드에 접근하여 변경하는 것을 방지할 수 있다. SELECT FOR UPDATE는 일반적으로 데이터를 읽은 후에 이를 업데이트하려는 상황에서 경쟁 조건을 방지하기 위해 사용된다.
SELECT FOR UPDATE를 실행하면 데이터베이스 시스템은 쿼리에 의해 반환된 모든 행에 대해 배타적 락을 건다. 이 락은 트랜잭션이 커밋되거나 롤백될 때까지 유지된다. 만약 다른 트랜잭션이 락이 걸린 행을 변경하려고 시도한다면, 그 트랜잭션은 락이 해제될 때까지 대기해야 한다.
예를 들어, 은행 계좌에서 금액을 인출하는 작업을 수행할 때 SELECT FOR UPDATE를 사용할 수 있다. 이 경우, 특정 계좌의 잔액을 확인하고 인출 작업을 수행하기 전에 해당 계좌 레코드에 대해 배타적 락을 설정하여 다른 트랜잭션이 동시에 같은 계좌를 수정하지 못하도록 할 수 있다.
조회 시점에 락이 필요한 경우는 언제일까?
트랜잭션 종료 시점까지 해당 데이터를 다른 곳에서 변경하지 못하도록 강제로 막아야 할 때 사용한다. 예를 들어서 애플리케이션 로직에서 memberA 의 금액을 조회한 다음에 이 금액 정보로 애플리케이션에서 어떤 계산을 수행한다. 그런데 이 계산이 돈과 관련된 매우 중요한 계산이어서 계산을 완료할 때 까지 memberA 의 금 액을 다른곳에서 변경하면 안된다. 이럴 때 조회 시점에 락을 획득하면 된다.
트랜잭션과 락은 데이터베이스마다 실제 동작하는 방식이 조금씩 다르기 때문에, 해당 데이터베이스 메뉴얼을 확 인해보고, 의도한대로 동작하는지 테스트한 이후에 사용해야 한다.
원자성(Atomicity): 트랜잭션 내의 모든 연산은 전부 완료되거나 전부 실행되지 않아야 한다는 원칙이다. 즉, 트랜잭션의 연산 중 하나라도 실패하면, 이미 실행된 모든 연산을 취소(롤백)하고, 전체 트랜잭션을 실패로 처리해야 한다.
일관성(Consistency): 트랜잭션이 실행되기 전과 후의 데이터베이스 상태는 일관된 상태를 유지해야 한다. 이는 트랜잭션이 데이터베이스의 모든 제약 조건을 충족시켜야 함을 의미한다. 예를 들어, 트랜잭션 동안 모든 데이터베이스 규칙과 제약 조건이 계속해서 만족되어야 한다.
독립성(Isolation): 동시에 여러 트랜잭션이 실행될 때, 각 트랜잭션은 서로 독립적으로 실행되어야 한다. 이는 한 트랜잭션이 다른 트랜잭션의 중간 실행 결과를 볼 수 없도록 보장해야 함을 의미한다. 독립성은 다양한 격리 수준을 통해 구현될 수 있다.
지속성(Durability): 트랜잭션이 성공적으로 완료되면, 그 결과는 시스템에 영구적으로 반영되어야 한다. 시스템에 장애가 발생하더라도, 이러한 변경 사항은 보존되어야 한다. 이는 데이터베이스 시스템이 장애로부터 복구된 후에도 트랜잭션 결과가 유실되지 않음을 보장한다.
ACID 속성 중 하나를 제외한 경우
ACID 속성 중 하나를 제외하고 시스템을 운영하게 되면, 데이터의 무결성, 신뢰성 및 시스템의 안정성에 심각한 문제가 발생할 수 있다. 각 속성이 제외될 때 발생할 수 있는 문제를 구체적으로 살펴보자.
원자성(Atomicity)이 제외된 경우
원자성이 보장되지 않으면, 트랜잭션 중 일부 연산만 성공적으로 완료되고 나머지는 실패할 수 있다. 이는 데이터의 일관성을 파괴하며, 데이터베이스가 부분적으로만 업데이트되어 부정확한 상태로 남을 수 있다. 예를 들어, 은행 계좌 이체 과정에서 금액을 출금하는 연산은 성공했으나 입금하는 연산은 실패하면, 금액이 사라지는 문제가 발생할 수 있다.
일관성(Consistency)이 제외된 경우
일관성이 보장되지 않으면, 트랜잭션 실행 후 데이터베이스의 상태가 모델의 규칙이나 제약 조건을 위반할 수 있다. 이는 데이터의 신뢰성을 저하시키며, 시스템의 예측 불가능한 동작을 초래할 수 있다. 예를 들어, 제약 조건을 무시하고 음수 잔액을 가진 계좌를 허용할 수 있다.
독립성(Isolation)이 제외된 경우
독립성이 보장되지 않으면, 여러 트랜잭션이 동시에 실행될 때, 한 트랜잭션의 중간 결과가 다른 트랜잭션에 의해 볼 수 있게 되어 데이터의 일관성이 손상될 수 있다. 이는 "더티 리드(Dirty Read)"와 같은 현상을 일으키며, 최종적으로 잘못된 데이터를 생성할 수 있다.
지속성(Durability)이 제외된 경우
지속성이 보장되지 않으면, 트랜잭션이 성공적으로 완료된 후에도 시스템 장애가 발생할 경우 해당 트랜잭션의 결과가 손실될 수 있다. 이는 데이터 손실로 이어지며, 복구가 어려울 수 있다. 예를 들어, 시스템이 트랜잭션 로그를 디스크에 쓰지 않고 장애가 발생하면, 그 사이에 실행된 모든 트랜잭션 데이터가 유실될 수 있다.
각 ACID 속성은 데이터베이스의 안정적인 운영을 위해 상호 의존적이며, 이 중 하나라도 결여되면 시스템 전체의 신뢰성과 정확성이 크게 저하될 수 있다. 따라서, 모든 속성을 적절히 관리하고 보장하는 것이 중요하다.
데이터베이스의 격리 수준(Isolation Level)
트랜잭션들이 데이터를 공유할 때 서로 영향을 주고받는 정도를 결정한다. 격리 수준은 데이터베이스의 동시성과 일관성 사이의 균형을 조절하는 중요한 역할을 하며, 다양한 격리 수준은 서로 다른 성능 및 ACID 속성에 대한 보장을 제공한다. 이러한 격리 수준에는 READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE 등이 있다.
READ UNCOMMITTED (읽기 미확정)
성능: 가장 높은 성능을 제공한다. 이는 트랜잭션들이 다른 트랜잭션의 확정되지 않은 변경 내용까지 읽을 수 있기 때문이다.
ACID 영향: 가장 낮은 격리 수준으로, 다른 트랜잭션에 의해 변경되었으나 아직 커밋되지 않은 데이터를 읽을 수 있어, "더티 리드(Dirty Read)" 문제가 발생할 수 있다. 이는 일관성(Consistency)을 저해할 수 있다.
READ COMMITTED (읽기 확정)
성능: READ UNCOMMITTED보다는 낮지만, 일반적으로 좋은 성능과 균형을 제공한다.
ACID 영향: "더티 리드" 문제를 방지할 수 있으며, 커밋된 데이터만 읽을 수 있다. 하지만, 같은 트랜잭션 내에서 동일한 쿼리를 두 번 실행하면 다른 결과를 얻을 수 있는 "Non-repeatable Read" 문제가 발생할 수 있다.
Non-repeatable Read ACID 속성 중 일관성(Consistency)과 관련된 문제로, 한 트랜잭션 내에서 같은 데이터를 두 번 조회했을 때, 두 조회 결과가 서로 다른 값을 반환하는 현상을 말한다. 이 문제는 주로 다른 트랜잭션이 동시에 동일한 데이터를 수정할 때 발생한다.
예를 들어, 트랜잭션 A가 어떤 데이터를 조회한 후, 같은 트랜잭션 내에서 동일한 데이터를 다시 조회하기 전에 트랜잭션 B가 그 데이터를 수정하고 커밋한다면, 트랜잭션 A의 첫 번째와 두 번째 조회 결과는 다를 것이다. 이러한 현상은 데이터베이스의 독립성(Isolation) 속성에 대한 위배로 간주될 수 있으며, 트랜잭션이 서로 격리되지 않아 발생하는 문제이다.
REPEATABLE READ (반복 가능 읽기)
성능: READ COMMITTED보다는 낮은 성능을 제공할 수 있다. 이는 트랜잭션이 실행되는 동안 조회한 데이터에 대해 다른 트랜잭션의 변경을 방지하기 때문이다.
ACID 영향: "Non-repeatable Read" 문제를 방지한다. 하지만, 다른 트랜잭션에 의해 새로 삽입된 데이터를 볼 수 있어 "팬텀 리드(Phantom Read)" 문제가 발생할 수 있다.
Phantom Read 데이터베이스 트랜잭션에서의 격리 수준(Isolation Level)과 관련된 현상으로, 한 트랜잭션 내에서 동일한 쿼리를 두 번 실행했을 때, 첫 번째 쿼리와 두 번째 쿼리의 결과가 다른 행(레코드)를 포함하는 경우를 말한다. 이는 다른 트랜잭션이 첫 번째와 두 번째 쿼리 실행 사이에 새로운 데이터를 삽입하거나 삭제함으로써 발생한다.
예를 들어, 트랜잭션 A가 특정 조건에 맞는 레코드들을 조회하는 쿼리를 실행하고, 이후 같은 트랜잭션 내에서 동일한 쿼리를 다시 실행했을 때, 트랜잭션 B가 첫 번째와 두 번째 쿼리 실행 사이에 해당 조건에 맞는 새로운 레코드를 삽입하면, 트랜잭션 A의 두 번째 쿼리 결과에는 처음에는 없던 새로운 레코드가 포함되게 된다. 이러한 현상을 팬텀 리드라고 한다.
SERIALIZABLE (직렬화 가능)
성능: 가장 엄격한 격리 수준으로, 성능 저하가 가장 크다. 이는 트랜잭션이 완전히 독립적으로 실행되어야 하며, 동시성이 크게 제한되기 때문이다.
ACID 영향: "팬텀 리드"를 포함한 모든 동시성 문제를 방지한다. 이 격리 수준에서는 트랜잭션이 다른 트랜잭션의 영향을 전혀 받지 않아 가장 높은 수준의 일관성을 보장한다.
읽기 미확정이 가장 낮은 격리 수준이고, 직렬화 가능이 가장 높은 격리 수준이다.
각 격리 수준은 동시성과 데이터 일관성 사이의 트레이드오프를 나타낸다. 높은 격리 수준은 더 나은 ACID 속성의 보장을 제공하지만, 동시성이 제한되어 성능에 영향을 줄 수 있다. 반대로 낮은 격리 수준은 더 높은 동시성과 성능을 제공하지만, 데이터 일관성 문제를 유발할 수 있다. 따라서, 애플리케이션의 요구 사항과 데이터 일관성의 중요도를 고려하여 적절한 격리 수준을 선택하는 것이 중요하다.
Trade-off 트레이드 오프(Trade-off)는 서로 상충하는 두 가지 이상의 목표나 조건 사이에서 선택을 해야 할 때, 한 쪽의 이득을 얻기 위해 다른 한 쪽을 포기하는 상황을 말한다. 즉, 어떤 선택을 할 때 발생하는 비용과 이득 사이의 균형을 의미한다.
일반적으로는 READ COMMITTED(커밋된 읽기) 트랜잭션 격리 수준을 많이 사용한다.
데이터베이스 연결 구조와 DB 세션
데이터베이스 연결 구조와 데이터베이스 세션은 데이터베이스 관리 시스템(DBMS)에서 중요한 개념이다.
이들은 클라이언트 애플리케이션과 데이터베이스 서버 간의 상호 작용을 가능하게 하는 기본적인 메커니즘을 제공한다.
데이터베이스 연결 구조
데이터베이스 연결 구조는 클라이언트 애플리케이션과 데이터베이스 서버 간의 통신 경로를 설정하는 방법을 말한다. 이 연결을 통해 클라이언트는 SQL 명령을 데이터베이스 서버로 전송할 수 있고, 서버는 이러한 명령을 실행한 후 결과를 클라이언트에게 반환한다.
연결은 일반적으로 TCP/IP 네트워크 프로토콜을 사용하여 이루어진다. 보안을 위해 SSL/TLS와 같은 암호화 프로토콜을 사용하여 데이터를 안전하게 전송할 수 있다.
클라이언트 애플리케이션은 데이터베이스 서버에 대한 연결을 맺을 때마다 상당한 오버헤드가 발생할 수 있다. 이를 최소화하기 위해 커넥션 풀을 사용한다. 커넥션 풀은 미리 정의된 수의 데이터베이스 연결을 유지하고, 필요할 때마다 이러한 연결을 재사용함으로써 연결 및 해제에 따른 오버헤드를 줄인다.
사용자는 웹 애플리케이션 서버(WAS)나 DB 접근 툴 같은 클라이언트를 사용해서 데이터베이스 서버에 접근할 수 있다.
클라이언트는 데이터베이스 서버에 연결을 요청하고 커넥션을 맺게 된다.
이때 데이터베이스 서버는 내부에 세션을 만든다. 그리고 앞으로 해당 커넥션을 통한 모든 요청은 이 세션을 통해서 실행하게 된다.
쉽게 이야기해서 개발자가 클라이언트를 통해 SQL을 전달하면 현재 커넥션에 연결된 세션이 SQL을 실행한다. 세션은 트랜잭션을 시작하고, 커밋 또는 롤백을 통해 트랜잭션을 종료한다. 그리고 이후에 새로운 트랜잭션을 다 시 시작할 수 있다.
사용자가 커넥션을 닫거나, 또는 DBA(DB 관리자)가 세션을 강제로 종료하면 세션은 종료된다.
커넥션 풀이 10개의 커넥션을 생성하면, 세션도 10개 만들어진다.
데이터베이스 세션
데이터베이스 세션은 클라이언트 애플리케이션과 데이터베이스 서버 간의 지속적인 대화나 상태를 유지하는 기간을 말한다.
세션은 클라이언트가 데이터베이스 서버에 연결을 맺은 후 시작되며, 클라이언트가 연결을 명시적으로 해제하거나, 타임아웃이 발생할 때 종료된다.
세션 상태: 데이터베이스 세션 동안, 서버는 클라이언트의 로그인 정보, 트랜잭션 상태, 설정 등 세션별 상태 정보를 유지한다. 이를 통해 클라이언트는 여러 데이터베이스 작업을 연속적으로 수행할 수 있으며, 각 작업은 동일한 세션 상태를 공유한다.
트랜잭션 관리: 세션 내에서 발생하는 여러 트랜잭션은 동일한 세션 컨텍스트 내에서 관리된다. 이는 트랜잭션의 ACID 속성을 보장하는 데 중요하다.
데이터베이스 연결 구조와 세션의 관리는 애플리케이션의 성능, 확장성, 보안성에 직접적인 영향을 미친다. 따라서, 효율적인 연결 및 세션 관리는 데이터베이스 기반 애플리케이션을 설계하고 구현할 때 중요한 고려 사항이다.
데이터베이스를 잘못 설계하면 불필요한 데이터 중복이 발생하여 릴레이션에 대한 데이터 삽입, 수정, 삭제 연산을 수행할 때 부작용이 발생할 수 있다.
이러한 현상을 이상(anomaly) 현상이라 한다.
이상 현상을 제거하면서 데이터베이스를 올바르게 설계해나가는 과정이 정규화다.
정규화는 데이터베이스를 설계 결과물을 검증하기 위해 사용하기도 한다.
이상 현상의 종류
이상 현상에는 갱신 이상(Modification Anomaly), 삽입 이상(Insertion Anomaly), 삭제 이상(Deletion Anomaly)이 있다.
위 릴레이션은 고객들이 이벤트에 참여한 결과를 저장하고 있는 릴레이션이다.
고객은 여러 이벤트에 참여할 수 있으므로 고객 아이디만으로는 튜플을 유일하게 식별할 수 없다.
따라서 고객아이디와 이벤트번호 속성을 함께 사용하여 이벤트참여 릴레이션의 기본키를 구성한다.
삽입 이상
릴레이션에 새 데이터를 삽입하기 위해 원치 않는 불필요한 데이터도 함께 삽입해야 하는 문제를 삽입 이상이라 한다.
이 릴레이션에 새로운 고객 데이터를 삽입한다고 하면, '성원용' 고객은 참여한 이벤트가 없으므로 삽입할 수 없다.
이벤트참여 릴레이션의 기본키가 고객아이디와 이벤트 번호 속성이고, 기본키를 구성하는 속성은 널 값을 가질 수 없다는 제약이 존재하기 때문이다.
따라서 성원용 고객에 대한 데이터를 이벤트 참여 릴레이션에 삽입하려면 실제로 참여하지 않은 임시 이벤트번호를 삽입해야 하므로 이벤트 참여 릴레이션에는 삽입 이상이 발생하게 된다.
갱신 이상
릴레이션의 중복된 튜플들 중 일부만 수정하여 데이터가 불일치하게 되는 모순이 발생하는 것을 갱신 이상이라 한다.
위 이벤트참여 릴레이션에는 아이디가 apple인 고객에 대한 튜플이 3개 존재하여, 고객아이디, 고객이름, 등급 속성의 값이 중복되어 있다.
아이디가 apple인 고객의 등급이 gold에서 vip로 변경된다면, 이벤트 참여 릴레이션에서 apple 고객에 대한 튜플 3개의 등급 속성 값이 모두 수정되어야 한다.
그렇지 않고 위와 같이 2개의 튜플만 등급이 수정된다면 모순이 생겨 갱신 이상이 발생하게 된다.
삭제 이상
릴레이션에서 튜플을 삭제하면 꼭 필요한 데이터까지 함께 삭제하여 데이터가 손실되는 연쇄 삭제 현상을 삭제 이상이라 한다.
아이디가 orange인 고객이 이벤트 참여를 취소하여 이벤트참여 릴레이션에서 관련된 튜플을 삭제해야 한다면, 위 그림같이 하나의 튜플을 삭제하면 된다.
하지만 이 튜플은 해당 고객에 대한 정보인 고객아이디, 고객이름, 등급에 대한 정보도 유일하게 가지고 있다.
따라서 이 튜플이 삭제되면 이벤트 참여와 관련이 없음에도 불구하고 해당 고객에 대한 중요한 데이터도 삭제되는 삭제 이상이 발생한다.
정규화의 필요성
위 이벤트참여 릴레이션과 같은 여러 이상 현상이 발생하는 이유는 관련 없는 속성들을 하나의 릴레이션에 모아두고 있기 때문이다.
이상 현상이 발생하지 않도록 하려면, 관련 있는 속성들로만 릴레이션을 구성해야 한다.
이를 위해 필요한 것이 정규화다.
정규화는 이상 현상이 발생하지 않도록, 릴레이션을 관련이 있는 속성들로만 구성하기 위해 릴레이션을 분해하는 과정이다.
정규화 과정에서 고려해야 하는 속성들 간의 관련성을 함수적 종속성(Functional Dependency)이라고 한다.
함수 종속(FD : Functional Dependency)
하나의 릴레이션을 구성하는 속성들의 부분 집합을 X와 Y라 할 때, 어느 시점에서든 릴레이션 내의 모든 튜플에서 X 값에 대한 Y 값이 항상 하나면 "X가 Y를 함수적으로 결정한다" 또는 "Y가 X에 함수적으로 종속되어 있다"라고 한다.
이 경우, X를 결정자, Y를 종속자라고 한다.
이 고객 릴레이션에서 각 고객아이디 속성 값에 대응되는 고객이름 속성과 등급 속성의 값이 단 하나이므로, 고객아이디가 고객이름과 등급을 결정한다고 볼 수 있다.
그러므로 고객 릴레이션에서 고객이름과 등급 속성은 고객아이디 속성에 함수적으로 종속되어 있어, 고객아이디는 결정자가 되고 고객 이름과 등급은 종속자가 된다.
함수 종속관계를 판단할 때 유의할 점은, 현재 시점에 릴레이션에 포함된 속성 값만으로 판단하면 안 된다는 것이다. 릴레이션에서 속성 값은 계속 변할 수 있기 때문에 속성 자체가 가지고 있는 특성과 의미를 기반으로 판단해야 한다.
고객 릴레이션에 현재 저장되어 있는 속성 값이 아닌 속성 자체의 특성을 고려하여 함수 종속관계를 판단해야 한다. 고객 릴레이션에서 고객아이디는 고객을 구별해주는 기본키 속성이기 때문에 아이디가 같은 서로 다른 고객이 존재할 수 없다. 그러므로 고객아이디가 정해지면 오직 하나의 고객이름과 등급이 결정된다.
일반적으로 튜플을 유일하게 구별하는 기본키와 후보키는 그 특성 때문에 릴레이션을 구성하는 다른 모든 속성들을 함수적으로 결정한다. 하지만 이러한 특성으로 인해 함수 종속관계 X →Y에서 기본키나 후보키만 결정자인 X가 될 수 있는 것은 아니다. 기본키나 후보키가 아니더라도 속성 값을 유일하게 결정하는 속성 X는 함수 종속관계에서 모두 결정자가 될 수 있다. 물론 릴레이션 내의 여러 튜플에서 속성 X 값이 같으면 이 값과 연관된 속성 Y 값도 모두 같아야 결정자로 인정받을 수 있다.
위의 이벤트참여 릴레이션에서는 고객아이디가 고객이름을 유일하게 결정한다. 고객아이디가 같으면 모든 튜플에서 고객이름이 반드시 같은 값을 가지기 때문이다. 그러므로 고객이름은 고객아이디에 종속되어 있어, 고객아이디가 결정자가 되고 고객이름이 종속자가 된다.
그리고 기본키인 [고객아이디, 이벤트번호] 속성 집합은 당첨여부 속성을 유일하게 결정한다. 아이디가 apple인 고객이 참여한 E001 이벤트의 당첨여부는 Y만 존재하기 때문이다. 그러므로 당첨여부는 [고객아이디, 이벤트번호]에 종속되어 있어, [고객아이디, 이벤트번호]가 결정자가 되고 당첨여부가 종속자가 된다. 물론 당첨여부뿐 아니라 고객이름도 기본키인[고객아이디, 이벤트번호]에 종속되어 있다.
이런 경우, 고객이름 속성이 [고객아이디, 이벤트번호] 속성 집합에 부분 함수 종속되었다고 한다.
반면 당첨여부 속성은 [고객아이디, 이벤트번호] 속성 집합에 완전 함수 종속되었다고 한다.
완전 함수 종속 : 릴레이션에서 속성 집합 Y가 속성 집합 X에 함수적으로 종속되어 있지만, 속성 집합 X 전체에 종속된 것인지 일부분에 종속된 것이 아님을 의미한다.
부분 함수 종속 : 속성 집합 Y가 속성 집합 X의 전체가 아닌 일부분에도 함수적으로 종속됨을 의미하며, 부분 함수 종속 관계가 성립하려면 결정자가 여러 개의 속성들로 구성되어 있어야 한다.
일반적으로 함수 종속이라고 하면 완전 함수 종속을 의미한다.
릴레이션에 존재하는 함수 종속 관계에서는 결정자와 종속자가 같거나, 결정자가 종속자를 포함하는 것처럼 당연한 함수 종속 관계는 고려하지 않는다.
기본 정규형과 정규화 과정
정규화의 개념과 정규형의 종류
정규화 : 함수 종속성을 이용하여 릴레이션을 연관성이 있는 속성들로만 구성되도록 분해해서, 이상 현상이 발생하지 않는 올바른 릴레이션으로 만들어나가는 과정
정규화의 기본 목표는 관련이 없는 함수 종속성을 별개의 릴레이션으로 표현하는 것이다.
정규형 : 릴레이션이 정규화된 정도를 표현한 것
정규형은 크게 기본 정규형 과 고급 정규형으로 나뉜다.
기본 정규형 : 제1정규형, 제2정규형, 제3정규형, 보이스/코드 정규형
고급 정규형 : 제4정규형, 제5정규형
각 정규형마다 만족시켜야 하는 제약조건이 존재한다.
릴레이션이 특정 정규형의 제약조건을 만족하면 릴레이션이 해당 정규형에 속한다고 표현한다.
정규형의 차수가 높아질수록 요구되는 제약조건이 많아지고 엄격해진다.
일반적으로 차수가 높은 정규형에 속하는 릴레이션일수록 데이터 중복이 줄어 데이터 중복에 의한 이상 현상이 발생하지 않는 바람직한 릴레이션일 수 있다.
하지만 모든 릴레이션이 제5정규형에 속해야 되는 것은 아니므로 릴레이션의 특성을 고려해서 적합한 정규형을 선택해야 한다.
제 1정규형(1NF)
제 1정규형 릴레이션에 속한 모든 속성의 도메인이 원자 값으로만 구성되어 있으면 제 1정규형에 속한다.
릴레이션이 제 1정규형에 속하려면 릴레이션에 속한 모든 속성이 더는 분해되지 않는 원자 값만 가져야 한다.
위 릴레이션에서 이벤트번호와 당첨여부 속성은 원자 값을 가지지 않는다.
즉, 다중 값을 가지는 속성을 포함하기 때문에 제 1정규형 제약조건을 만족시키지 못하므로 제 1정규형에 속하지 않는다.
관계 데이터베이스 릴레이션은 모든 속성이 원자 값을 가지는 특성이 있기 때문에 최소한 제1 정규형을 만족해야 관계 데이터베이스의 릴레이션이 될 자격이 있다고 말할 수 있다.
아래의 릴레이션은 이벤트번호와 당첨여부 속성은 원자 값을 갖도록 수정한 릴레이션이다.
수정한 릴레이션은 제 1정규형에 속하지만 불필요한 데이터 중복으로 인해 이상 현상이 발생할 수 있다.
이벤트 참여 릴레이션에는 등급과 할인율 속성의 값이 중복되어 나타나는 경우가 많다.
이처럼 불필요한 데이터가 중복되면 이상 현상이 발생할 수 있다.
이벤트참여 릴레이션은 부분 함수 종속을 포함하고 있기 때문이 이상 현상이 발생한다.
즉, 기본키인 [고객아이디, 이벤트번호]에 완전 함수 종속되지 못하고 일부분인 고객아이디에 종속되는 등급과 할인율 속성 때문이다.
따라서 부분 함수 종속이 제거되도록 이벤트참여 릴레이션을 분해해야 한다.
릴레이션을 분해하여 부분 함수 종속을 제거하면, 분해된 릴레이션들은 제2 정규형에 속하게 된다.
제 2정규형(2NF)
제 2정규형 릴레이션이 제 1정규형에 속하고, 기본키가 아닌 모든 속성이 기본키에 완전 함수 종속되면 제 2정규형에 속한다.
위와 같이 2개의 릴레이션으로 분해하면, 분해된 고객 릴레이션과 이벤트참여 릴레이션은 모두 제2정규형에 속하게 된다. 릴레이션이 둘로 분해되면서 등급과 할인율 속성에 대한 데이터 중복이 줄어듦을 확인할 수 있다.
위의 함수 종속 다이어그램에서 확인할 수 있듯이, 릴레이션 분해 과정을 통해 고객 릴레이션에는 기본키인 고객아이디와 기본키에 완전 함수 종속된 등급·할인율 속성만 존재한다. 그러므로 고객 릴레이션은 제2정규형에 속한다.
이벤트참여 릴레이션에도 기본키인 [고객아이디, 이벤트번호]와 기본키에 완전 함수 종속된 당첨여부 속성만 존재한다. 그러므로 이벤트참여 릴레이션도 제2정규형에 속한다.
정규화 과정에서 릴레이션을 분해할 때 주의할 점은, 분해된 릴레이션들을 자연 조인하여 분해 전의 릴레이션으로 다시 복원할 수 있어야 한다는 것이다.
즉, 릴레이션이 의미상 동등한 릴레이션들로 분해되어야 하고, 릴레이션을 분해했을 때 정보 손실이 발생하지 않아야 한다.
정보의 손실 없이 릴레이션을 분해하는 것을 무손실 분해라고 한다.
제 2정규형에 속하더라도 릴레이션에 이상 현상이 발생할 수 있다.
위 이벤트참여 릴레이션은 함수 종속성을 단 하나만 포함하므로 이상 현상이 발생하지 않는다.
하지만 고객 릴레이션은 부분 함수 종속성은 없지만 함수 종속성을 아직도 여러 개 포함하고 있어 이상 현상이 발생할 수 있다.
릴레이션을 분해하여 이행적 함수 종속을 제거하면, 분해된 릴레이션들은 제 3정규형에 속하게 된다.
제 3정규형(3NF)
제 3정규형 릴레이션이 제 2정규형에 속하고, 기본키가 아닌 모든 속성이 기본키에 이행적 함수 종속이 되지 않으면 제 3정규형에 속한다.
이행적 함수 종속이란 X→Y, Y→Z의 함수 종속 관계를 가지는 상황에서 X→Z가 성립하게 되는 상황을 의미한다.
제2정규형을 만족하더라도 하나의 릴레이션에 함수 종속 관계가 여러개 있고 그중에서도 이행적 함수 종속 관계를 가지게 되면 이상 현상이 발생할 수 있다.
따라서 이행적 함수 종속이 발생하지 않도록 릴레이션을 분해해야 한다.
보이스/코드 정규형(BCNF : Boyce/Codd Nomal Form)
보이스/코드 정규형(BCNF) 릴레이션의 함수 종속 관계에서 모든 결정자가 후보키이면 보이스/코드 정규형에 속한다.
제3정규형까지 마치고 나면 하나의 릴레이션에 여러개의 후보키가 존재할 수도 있는데, 이 경우에은 제3정규형까지 모두 만족하더라도 이상 현상이 발생할 수 있다.
따라서 후보키가 여러개 있을때 발생할 수 있는 이상 현상을 방지하기 위해 보이스/코드 정규형을 하게 된다.따라서 제3정규형의 릴레이션에서 기본키가 유일한 후보키이며 함수 종속 관계어서도 유일한 결정자가 되도록 릴레이션을 분해한다.
제4정규형과 제5정규형
고급 정규형으로 분류되는 제4정규형은 릴레이션이 보이스/코드 정규형을 만족하면서, 함수종속이 아닌 다치 종속을 제거해야 만족할 수 있다.
그리고 제5정규형은 릴레이션이 제4정규형을 만족하면서 후보키를 통하지 않는 조인 종속을 제거해야 만족할 수 있다.
다만 오히려 제5정규형까지 만족할 때까지 분해할 경우 비효율적일 때가 많다.
정규화 과정 정리
릴레이션을 분해하여 모든 속성의 도메인이 원자 값으로만 구성되도록 하면 제1정규형이 된다.
제1정규형에 속하는 릴레이션에서 부분 함수 종속을 제 거하여 기본키가 아닌 모든 속성이 기본키에 완전 함수 종속되면 제2정규형이 된다.
DB의 설계 단계에서는 엔터티(Entity), DBMS로 구현되는 단계에서는 테이블(Table), 개념 단계에서 엔터티 간 연관관계를 릴레이션(Relation)이라고 한다.
엔티티 > 테이블 > 릴레이션 순으로 보면된다. 모든 릴레이션은 테이블이지만, 모든 테이블이 릴레이션인 것은 아니다. 모든 테이블은 엔티티이지만, 모든 엔티티가 테이블인것은 아니다.
관계 데이터 모델에서는 하나의 개체에 관한 데이터를 릴레이션(relation) 하나에 담아 데이터 베이스에 저장한다.
관계형 데이터베이스에서의 "릴레이션"은 데이터베이스 테이블의 구조를 설명하는 데 사용되는 개념이다. 릴레이션 (Relation): 관계형 데이터베이스에서 릴레이션은 테이블을 나타낸다. 테이블은 행과 열로 이루어진 데이터 구조이다. 각 테이블은 여러 개의 열(속성 또는 필드)과 이에 해당하는 데이터 레코드(행)로 구성된다. 이러한 행과 열의 집합은 릴레이션으로 간주된다.
Attribute (열)
릴레이션의 열(column)을 속성 또는 애트리뷰트(attribute)라고 부른다.
각 속성은 서로 다른 이름을 이용해 구별한다.
릴레이션은 파일 시스템에서의 파일, 속성은 해당 파일의 필드(field)에 대응하는 개념이다.
Tuple (행)
릴레이션의 행을 튜플(tuple)이라 부른다.
튜플은 개체의 인스턴스다.
튜플은 파일 시스템에서의 파일의 레코드(record)에 대응하는 개념이다.
Domain
속성 하나가 가질 수 있는 모든 값의 집합을 해당 속성의 도메인(domain)이라 한다.
예를 들어 학년 속성의 데이터 타입이 정수형이고 해당 속성에서 취할 수 있는 값의 범위가 1~4까지 라면, 1~4라는 범위는 해당 속성에 지정된 정수형의 모든 범위가 아니라 일부분이므로 사용자는 1~4까지의 범위를 해당 속성의 도메인으로 정의해서 사용할 수 있다는 의미이다.
즉, 도메인은 각 속성이 가질 수 있도록 허용된 값들의 집합이다.
테이블의 컬럼 값을 구성할 때 값의 범위, 데이터타입, 제약사항 등을 설정하는데 그 범위 값의 설정을 도메인이라 생각하면 된다.
NULL 값
릴레이션에 있는 특정 튜플의 속성 값을 모르거나, 적합한 값이 없는 경우에는 널이라는 특별한 값을 사용할 수 있다.
널 값은 특정 속성에 해당되는 값이 없음을 나타내므로 숫자 0이나 공백 문자와는 다르다.
Degree (차수)
하나의 릴레이션에서 속성의 전체 개수를 릴레이션의 차수라고 한다.
모든 릴레이션은 최소 1 이상의 차수를 유지해야 한다.
릴레이션의 차수는 일반적으로 자주 변하지 않는다는 정적인 특징이 있다.
Cardnality(카디널리티)
하나의 릴레이션에서 튜플의 전체 개수를 릴레이션의 카디널리티라고 한다.
튜플이 없는 릴레이션이 존재할 수도 있다.
릴레이션의 카디널리티는 일반적으로 자주 변한다는 동적인 특징이 있다.
릴레이션과 데이터베이스의 구성
관계 데이터 모델에서 릴레이션은 릴레이션 스키마와 릴레이션 인스턴스로 구성되어 있다.
릴레이션 스키마
릴레이션 스키마(relation schema)는 릴레이션의 이름과 릴레이션에 포함된 모든 속성의 이름으로 정의하는 릴레이션의 논리적 구조다.
릴레이션 스키마는 DBMS가 내부적으로 데이터 정의어를 이용해 정의하지만, 일반적으로는 아래와 같은 형태로 쉽게 표현한다.
릴레이션 스키마는 릴레이션 내포(relation intension)라고 부른다.
릴레이션 인스턴스
릴레이션 인스턴스(relation instance)는 어느 한 시점에 릴레이션에 존재하는 튜플들의 집합이다.
릴레이션 인스턴스에 포함된 튜플은 릴레이션 스키마에서 정의하는 각 속성에 대응하는 실제 값으로 구성되어 있다.
릴레이션 인스턴스를 보면 현재 릴레이션의 실제 내용을 쉽게 파악할 수 있다.
릴레이션 인스턴스는 간단히 릴레이션이라 부르기도 하고 릴레이션 외연(relation extension)이라고도 부른다.
데이터베이스 스키마와 데이터베이스 인스턴스
일반적으로 데이터베이스는 릴레이션 여러 개로 구성된다.
데이터베이스의 전체 구조를 의미하는 데이터베이스 스키마는 데이터베이스를 구성하는 릴레이션의 스키마를 모아놓은 것이다.
즉, 특정 데이터베이스 스키마를 설계한다는 것은 모든 필요한 릴레이션의 스키마를 모두 정의한다는 뜻이다.
데이터베이스 인스턴스는 어느 한 시점에서 데이터베이스에 저장된 데이터 내용의 전체 집합을 의미한다.
즉, 데이터베이스를 구성하는 모든 릴레이션의 인스턴스를 모아놓은 것이다.
릴레이션의 특성
튜플의 유일성
하나의 릴레이션에는 동일한 튜플이 존재할 수 없다.
하나의 릴레이션에 똑같은 튜플이 있으면 안 되고, 모든 튜플에는 다른 튜플과 구별되는 유일한 특성이 있어야 한다.
릴레이션을 튜플의 모임인 집합의 개념으로 이해한다면, 하나의 집합에 동일한 원소가 존재할 수 없다는 특성과 연관 지어 생각할 수 있다.
튜플을 유일하게 구별하기 위해 선정하는 속성(또는 속성들의 모임)을 키(key)라고 부른다.
튜플의 무순서
하나의 릴레이션에서 튜플 사이의 순서는 무의미하다.
튜플 순서가 바뀐다고 다른 릴레이션이 될 수 없고, 순서와 상관없이 튜플 내용이 같아야 같은 릴레이션이다.
데이터베이스는 위치가 아닌 내용으로 검색되므로 튜플의 순서는 중요하지 않다.
릴레이션에는 튜플이 삽입 순서에 따라 저장되지만, 효율적인 처리를 위해 튜플의 순서를 임의로 바꾸기도 한다.
속성의 무순서
하나의 릴레이션에서 속성 사이의 순서는 무의미하다.
속성은 순서가 바뀌어도 다른 릴레이션이 될 수 없고, 순서와 상관없이 같은 속성들로 구성되어 있어야 같은 릴레이션이다.
속성의 원자성
모든 속성 값은 더는 분해할 수 없는 하나의 값, 즉 원자 값만 가질 수 있다.
하나의 속성은 여러 개의 값, 즉 다중 값을 가질 수 없다.
위와 그림과 같은 고객 릴레이션은 회사원, 학생과 같이 값이 여러 개인 직업 속성을 포함하므로 관계 데이터 모델의 릴레이션으로 적합하지 않다.
물론 현실에서는 직업이 둘 이상인 고객이 존재할 수 있지만, 관계 데이터 모델은 이런 복잡한 개념을 배제하고 릴레이션을 단순한 구조로 정의하고자 하는 특징이 있어 다중 값을 허용하지 않는다.
키의 종류
튜플을 유일하게 구별하기 위해 모든 속성을 이용하는 것보다 일부 속성만 이용하는 것이 효율성을 높일 수 있다.
릴레이션에 포함된 튜플들을 유일하게 구별해주는 역할은 속성 또는 속성들의 집합인 키가 담당한다.
키는 관계 데이터 모델에서 중요한 제약조건을 정의한다.
유일성 : 하나의 릴레이션에서 키로 지정된 속성 값은 튜플마다 달라야 한다는 특성
최소성 : 꼭 필요한 최소한의 속성들로만 키를 구성하는 특성
관계 데이터 모델에서는 키를 아래와 같이 슈퍼키, 후보키, 기본키, 대체키, 외래키의 다섯 가지로 분류할 수 있다.
슈퍼키(Super Key)
슈퍼키는 유일한 특성을 만족하는 속성 또는 속성들의 집합이다.
유일성은 키가 갖추어야 하는 기본 특성으로, 하나의 릴레이션에서 키로 지정된 속성 값은 튜플마다 달라야 한다는 의미다.
즉, 키 값이 같은 튜플은 존재할 수 없다.
후보키(Candidate Key)
후보키는 유일성과 최소성을 만족하는 속성 또는 속성들의 집합이다.
최소성은 꼭 필요한 최소한의 속성들로만 키를 구성하는 특성이다.
그러므로 하나의 속성으로 구성된 키는 당연히 최소성을 만족한다.
기본키(Primary Key)
릴레이션에서 튜플을 구별하기 위해 여러 개의 후보키를 모두 사용할 필요는 없다.
데이터베이스 설계자나 관리자는 여러 후보키 중에서 기본적으로 사용할 키를 반드시 선택해야 하는 데 이것이 기본 키다.
만약, 후보키가 1개만 존재한다면 당연히 해당 후보키를 기본키로 선택해야 하겠지만 여러 개일 경우에는 데이터베이스 사용 환경을 고려하여 적합한 것을 기본키로 선택하면 된다.
선택한 기본키는 속성 이름에 밑줄을 그어 표현한다.
후보키 중에서 기본키를 선택하는 기준은 아래와 같다.
널 값을 가질 수 있는 속성이 포함된 후보키는 기본키로 부적합하다.
값이 자주 변경될 수 있는 속성이 포함된 후보키는 기본키로 부적합하다.
단순한 후보키를 기본키로 선택한다.
대체키(Alternate Key)
대체키는 기본키로 선택되지 못한 후보키다.
대체키는 기본키를 대신할 수 있지만 기본 키가 되지 못하고 탈락한 이유가 있을 수 있다.
외래키(Foreign Key)
외래키는 어떤 릴레이션에 소속된 속성 또는 속성 집합이 다른 릴레이션의 기본키가 되는 키다.
즉, 다른 릴레이션의 기본키를 그대로 참조하는 속성의 집합이 외래키다.
외래키는 릴레이션들 사이의 관계를 올바르게 표현하기 위해 필요하다.
외래키가 다른 테이블의 대체키를 참조하는 것도 가능하다. 기본키로 선택받지 못했지만 유일성과 최소성을 만족하는 대체키를 참조하더라도 관련 있는 튜플을 구분할 수 있기 때문이다.
하나의 릴레이션에는 외래키가 여러 개 존재할 수도 있다.
외래키를 기본키로 사용할 수도 있고 외래키를 포함하여 기본키를 구성할 수도 있다.
외래키가 다른 릴레이션의 기본키를 참조하는 키라고 정의했지만 반드시 다른 릴레이션을 참조할 필요는 없다.
참조하는 릴레이션과 참조되는 릴레이션이 같을 수도 있다.
즉, 외래키 자신이 속한 릴레이션의 기본키를 참조하도록 외래키를 정의할 수도 있다.
외래키는 다른 릴레이션의 기본키를 참조하지만 이 릴레이션에서는 기본키가 아니기 때문에 널 값을 가질 수 있다.
관계 데이터 모델의 제약
관계 데이터 모델에서 정의하고 있는 기본 제약 사항은 키와 관련한 무결성 제약조건이다.
무결성은 데이터에 결함이 없는 상태, 즉 데이터가 정확하고 유효하게 유지된 상태를 말한다.
무결성 제약조건의 주요 목적은 데이터 베이스에 저장된 데이터의 무결성을 보장하고, 데이터베이스의 상태를 일관되게 유지하는 것이다.
데이터베이스가 삽입, 삭제, 수정 연산으로 상태가 변하더라도 무결성 제약조건은 반드시 지켜져야 한다.
관계 데이터 모델이 기본으로 포함하고 있는 무결성 제약조건에는 개체 무결성 제약조건과 참조 무결성 제약조건이 있다.
데이터 베이스의 상태를 일관성 있게 유지하기 위해서는 두 가지를 모두 만족시켜야 한다.
개체 무결성 제약조건(Entity Integrity Constraint)
개체 무결성 제약 조건은 기본키를 구성하는 모든 속성은 널 값을 가지면 안 된다는 규칙이다.
아래의 고객 릴레이션은 개체 무결성 제약조건을 위반한 예가 된다.
그러므로 이 상태의 릴레이션은 실제로 존재할 수 없다.
개체 무결성 제약조건을 만족시키려면 새로운 튜플이 삽입되는 연산과 기존의 튜플의 기본키 속성 값이 변경되는 연산이 발생할 때 기본키에 널 값이 포함되는 상황에서는 연산의 수행을 거부하면 된다.
새로운 튜플(레코드)이 삽입되는 경우 기본키 속성에 널(null) 값이 포함된 새로운 행을 추가하려고 할 때, 데이터베이스는 이 작업을 거부해야 한다. 즉, 기본키는 널 값을 허용하지 않으므로 새로운 레코드가 추가될 때 기본키 속성에는 반드시 유효한 값이 포함되어야 한다.
기존의 튜플(레코드)의 기본키 속성 값이 변경되는 경우 이미 존재하는 레코드의 기본키 속성 값이 널(null)이거나 변경될 때, 이 변경 작업은 거부되어야 한다. 기본키는 해당 레코드를 고유하게 식별하는 데 사용되므로, 기본키 속성은 항상 유효한 값을 가져야 하며, 변경되는 과정에서 널 값이 들어가면 안 된다.
즉, 개체 무결성을 유지하기 위해서는 기본키 속성에 널(null) 값을 허용하지 않고, 새로운 레코드 삽입 또는 기존 레코드의 기본키 속성 값 변경 시에 이를 감지하고 거부하는 것이 중요하다.
이것은 일반 사용자가 직접 수행하기보다는 DBMS가 자동으로 수행하므로 새로운 릴레이션을 생성할 때마다 기본키를 어떤 속성들로 구성할 것인지 DBMS에게 알려주면 된다.
참조 무결성 제약조건(Referential Integrity Constraint)
개체 무결성 제약조건이 기본키에 대한 규칙으로 각 릴레이션마다 적용된다면, 참조 무결성 제약조건은 외래키에 대한 규칙으로 연관된 릴레이션들에 적용된다.
참조 무결성 제약조건이란 외래키는 참조할 수 없는 값을 가질 수 없다는 규칙이다.
외래키는 다른 릴레이션의 기본키를 참조하는 속성이고 릴레이션 간의 관계를 표현하는 역할을 한다.
그런데 외래키가 자신이 참조하는 릴레이션의 기본키와 상관이 없는 값을 가지게 되면 두 릴레이션을 연관시킬 수 없으므로 외래키 본래의 의미가 없어진다.
그러므로 외래키는 자신이 참조하는 릴레이션에 기본키 값으로 존재하는 값, 즉 참조 가능한 값만 가져야 한다.
아래의 그림은 참조 무결성 제약조건을 위반한 예이다.
외래키가 널 값을 가진다고 해서 참조 무결성 제약조건을 위반했다고 말할 수 없다.
위 그림에서 주문고객 속성 값이 널이라는 것은 주문한 고객이 누구인지 모를 뿐, 고객 릴레이션에 존재하지 않는 고객이 주문한 것으로 판단하기는 어렵기 때문이다.
또한 참조 릴레이션에 존재하는 튜플을 삭제하는 연산은 참조 무결성 제약 조건을 위반하지 않는 경우에만 수행한다.
위 그림을 예로 들면, 고객 릴레이션의 apple이라는 아이디를 가진 고객을 삭제해 버리면 주문 릴레이션의 주문고객의 apple가 null로 바뀌어 버린다. 이는 참조 무결성 제약조건을 위반한 것이다.
마지막으로 참조 릴레이션에 존재하는 기본키의 속성 값이 변경될 때 참조 무결성 제약 조건을 위반하지 않는지 확인해야 한다.
위 그림을 예로 들면, 고객 릴레이션의 기본키인 고객 아이디 속성의 값을 바꾸면 주문 릴레이션에 원래의 속성 값으로 남아 있게 된다. 이는 참조 무결성 제약조건을 위반한 것이다.
이럴 때는 변경 연산을 수행하지 않거나, 주문 릴레이션에 남아 있는 관련 튜플에서 주문고객 속성의 값을 새로운 값으로 함께 변경해야 참조 무결성 제약조건을 만족시킬 수 있다.
흔히 데이터베이스, 데이터베이스 관리 시스템, 데이터베이스 시스템을 같은 의미로 사용하지만 각 용어의 개념을 구분하고 관계를 명확히 이해할 필요가 있다.
데이터베이스(DB) : 데이터를 저장해 두는 곳, 데이터의 집합
데이터베이스 관리 시스템(DBMS) : 데이터베이스에 저장된 데이터가 일관되고 무결한 상태로 유지되도록 관리
데이터베이스 시스템(DBS) : 데이터베이스와 데이터베이스 관리 시스템을 이용해 조직에 필요한 정보를 제공해 주는 전체 시스템
데이터베이스와 데이터베이스 관리 시스템은 데이터베이스 시스템의 핵심 구성 요소다.
데이터베이스 시스템은 위에서 설명한 두 가지 말고도 데이터 언어, DB와 DBMS를 설치하고 데이터 처리 연산을 담당하는 컴퓨터로 구성된다.
데이터베이스의 구조
스키마
스키마는 DB에 저장되는 데이터 구조와 제약조건을 정의한 것이다.
위 그림은 스키마를 그림으로 간략하게 표현한 것이다.
고객과 관련된 데이터인 고객번호, 이름, 나이, 주소를 저장한다고 가정한다.
고객번호는 정수로, 이름은 최대 10자의 문자열로, 나이는 정수로, 주소는 최대 20자의 문자열만 허용하기로 했다면 이 모든 정해진 내용이 스키마다.
정의된 스키마에 따라 DB에 실제로 저장된 값이 인스턴스다.
보통 스키마는 한번 정의되면 자주 변경되지 않지만(정적), 인스턴스는 계속 변하는(동적) 특성이 있다.
DB 테이블
출처 : 해시넷
3단계 데이터베이스 구조
3단계 데이터베이스 구조의 개념
데이터베이스를 3단계로 나누어 이해한다는 개념이다.
외부 단계 : 개별 사용자 입장에서 바라보는 관점
개념 단계 : 조직 전체의 입장에서 바라보는 관점
내부 단계 : 물리적인 저장 장치의 입장에서 바라보는 관점
3단계 데이터베이스 구조를 통해, 모든 데이터의 저장 및 유지와 관련된 복잡한 내용을 숨기고 필요한 데이터만 단순화한 외부 단계의 관점을 일반 사용자들에게 제공할 수 있다.
유선이는 102호의 구조나 크기, 인테리어 등에 관심이 있을 뿐 명석이가 사는 301호나 다른 집이 어떤지는 관심이 없다. 이렇게 유선이나 명석이 관점에서 아파트를 바라보는 것이 외부 단계다.
아파트 관리인은 어느 한 집에만 관심을 두면 안 된다. 아파트를 문제없이 관리하려면 아파트 전체를 잘 알고 있어야 하는데, 이처럼 관리인 관점에서 전체 아파트를 바라보는 것이 개념 단계다.
아파트 건설 업체는 아파트 뼈대, 즉 철근 콘크리트 구조가 어떻게 생겼는지, 시멘트를 얼마나 사용했는지 등을 알고 있어야 하는데, 이처럼 아파트 건설 업체관점에서 전체 아파트를 바라보는 것이 내부 단계이다.
외부 단계 (뷰)
외부 단계에서는 개별 사용자 관점에서 데이터베이스를 이해하고 표현한다.
고객 관리를 담당하는 직원은 고객과 관련된 데이터에만, 상품 관리를 담당하는 직원은 상품과 관련된 데이터에만 관심을 가질 것이다.
외부 단계에서는 개별 사용자가 데이터베이스를 어떻게 보는가를 표현하므로 사용자마다 생각하는 데이터베이스 구조가 다르다.
이처럼 외부 단계에서 사용자에게 필요한 데이터베이스를 정의한 것을 외부 스키마라 한다.
외부 스키마는 각 사용자가 생각하는 데이터베이스의 모습을 표현한 논리적인 구조로, 사용자마다 다르다.
하나의 데이터 베이스에는 외부 스키마가 여러 개 존재할 수 있고, 외부 스키마 하나를 사용 목적이 같은 사용자들이 공유할 수 있다.
외부 스키마는 전체 데이터베이스 중 사용자가 관심을 가지는 일부분으로 볼 수 있어 서브 스키마라고도 한다.
개념 단계 (테이블)
개념 단계에서는 데이터베이스를 이용하는 사용자들의 관점을 통합하여, 데이터베이스를 조직 전체의 관점에서 이해하고 표현한다.
DBMS나 관리자의 관점에서 모든 사용자에게 필요한 데이터를 통합하여 전체 데이터베이스의 논리적 구조를 정의한다.
이를 개념 스키마라고 한다.
개념 스키마는 조직 전체의 관점에서 생각하는 데이터베이스의 모습이며, 모든 개별 사용자가 생각하는 데이터베이스의 모습을 하나로 합친 형태다.
개념 스키마는 전체 데이터베이스에 어떤 데이터가 저장되는지, 데이터들 간에는 어떤 관계가 존재하고 어떤 제약조건이 있는지에 대한 정의뿐만 아니라, 데이터에 대한 보안 정책이나 접근 권한에 대한 정의도 포함한다.
하지만 데이터를 물리적으로 저장하는 방법이나 데이터 저장 장치와는 독립적이다.
하나의 데이터베이스에는 개념 스키마가 하나만 존재하고, 각 사용자는 개념 스키마의 일부분을 사용한다.
즉, 외부 스키마는 개념 스키마를 기초로 하여 사용자의 이용 목적에 맞게 만들어진다.
일반적으로 스키마라고 하면 개념 스키마를 의미한다.
내부 단계 (인덱스)
내부 단계에서는 데이터베이스를 디스크나 테이프 같은 저장 장치의 관점에서 이해하고 표현한다.
즉, 내부 단계에서는 전체 데이터베이스가 저장 장치에 실제로 저장되는 방법을 정의하며 이를 내부 스키마라고 한다.
내부 스키마는 파일에 데이터를 저장하는 레코드의 구조, 레코드를 구성하는 필드 크기, 인덱스를 이용한 레코드 접근 경로 등을 정의한다.
내부 스키마는 데이터베이스의 개념 스키마에 대한 물리적인 저장 구조를 표현하므로 하나의 데이터베이스에 하나만 존재한다.
외부 단계에는 고객 분석팀과 상품 배송팀 사용자가 존재한다.
두 사용자는 자신의 팀에 필요한 데이터로 구성된 외부 스키마를 각각 가지고 있다.
외부 단계에서는 사용자별로 외부 스키마를 정의하여 불필요한 데이터 접근을 사전에 막아 보안 측면에서도 효과적이다.
개념 단계에는 고객 데이터베이스 전체에 대한 논리적 구조를 정의하는 개념 스키마가 하나 존재한다.
내부 단계에는 고객 데이터베이스를 저장 장치에 저장하는 파일의 레코드 구조를 정의한 내부 스키마가 하나 존재한다.
내부 스키마에 정의된 고객 레코드는 필드 7개로 구성되어 있고, 레코드 총 길이는 70바이트다. 이 내부 스키마는 번호와 연락처 필드에 인덱스를 정의하고 있어, 번호나 연락처 필드의 값을 이용해 해당 고객 레코드에 빠르게 접근할 수 있다.
데이터 독립성
하나의 데이터베이스에는 세 가지 유형의 스키마가 존재하지만, 각각의 스키마는 데이터베이스를 바라보는 관점이 다를 뿐 모드 같은 데이터베이스를 표현한다.
실제 데이터는물리적 저장 장치에 저장된 데이터베이스에만 존재하므로 사용자가 자신의 외부 스키마를 통해 원하는 데이터를 얻으려면 내부 스키마에 따라 저장된 데이터베이스에 접근해야 한다.
그러므로 세가지 스키마 사이에는 유기적인 대응 관계가 성립해야 한다.
상품 배송팀의 외부 스키마에 있는 고객번호 데이터는 개념 스키마에 있는 번호 데이터에 대응하고, 개념 스키마에 있는 번호 데이터는 내부 스키마에 있는 번호 필드에 대응한다는 연결 관계가 미리 정의되어 있어야 한다.
그래야 사용자가 물리적 저장장치에 저장된 고객번호 데이터에 접근할 수 있다.
스키마 사이의 대응 관계를 사상 또는 매핑이라 한다.
외부 스키마와 개념 스키마는 외부/개념 사상에 의해 대응되고, 개념 스키마와 내부 스키마는 개념/내부 사상에 의해 대응된다.
DBMS는 미리 정의된 외부/개념 사상과 개념/내부 사상 정보를 이용해 사용자가 원하는 데이터에 접근할 수 있다.
데이터베이스를 3단계 구조로 나누고, 단계별로 스키마를 유지하며 스키마 사이의 대응 관계를 정의하는 궁극적인 목적은 데이터 독립성을 실현하기 위해서다.
데이터 독립성은 DBMS의 중요한 장점이자 DBMS이 필요한 이유이기도 하다.
데이터 독립성은 하위 스키마가 변경하더라도 상위 스키마가 영향을 받지 않는 특성이다.
3단계 데이터베이스 구조에는 논리적 데이터 독립성과 물리적 데이터 독립성이 존재한다.
논리적 데이터 독립성
논리적 데이터 독립성은 개념 스키마가 변경되더라도 외부 스키마가 영향을 받지 않는 것이다.
그래서 전체 데이터베이스의 논리적인 구조가 변경되어도 관련된 외부/개념 사상정보만 적절히 수정해 주면 직접 관련이 없는 사용자를 위한 외부 스키마는 변경할 필요가 없다.
응용 인터페이스 외부/개념 사상은 외부 스키마와 개념 스키마의 대응 관계를 정의한 것으로, 응용 인터페이스라고도 한다.
개념 스키마가 변경되어도 외부 스키마가 영향을 받지 않는다는 것은 결국 외부 스키마의 사용자가 전체 데이터베이스의 논리적 구조가 변경되었다는 사실을 알 필요가 없음을 의미한다.
사용자는 응용 프로그램을 통해 데이터베이스 시스템을 이용하는 경우가 많으므로 사용자와 응용 프로그램을 동일하게 생각한다.
물리적 데이터 독립성
물리적 데이터 독립성은 내부 스키마가 변경되더라도 개념 스키마가 영향을 받지 않는 것이다.
그래서 결과적으로 외부 스키마도 영향을 받지 않는다.
물리적 데이터 독립성이 실현되면 데이터베이스의 저장 구조가 변경되어도 관련된 개념/내부 사상 정보만 적절히 수정해 주면 직접적으로 관련이 없는 데이터베이스의 논리적 구조는 영향을 받지 않는다.
저장 인터페이스 개념/내부 사상은 개념 스키마와 내부 스키마의 대응 관계를 정의한 것으로 저장 인터페이스라고도 한다.
데이터 사전
저장된 데이터를 올바르게 관리하고 이용하려면 필요한 부가 정보도 저장해야 한다.
대표적인 부가 정보가 스키마와 사상 정보다.
데이터 독립성을 실현하면서 데이터베이스를 다양한 관점에서 이해하기 위해 정의되는 세 가지 스키마에 대한 정보와 스키마 간의 사상 정보도 어딘가에 저장되어 있어야 필요할 때 사용할 수 있다.
데이터베이스에 저장되는 데이터에 관한 정보를 저장하는 곳을 데이터 사전 또는 시스템 카탈로그라고 한다.
데이터 사전은 일반 사전처럼 데이터베이스에 저장되어 있는 데이터를 정확하고 효율적으로 이용하기 위해 참고해야 되는 스키마, 사상 정보, 다양한 제약조건 등을 저장하고 있다.
메타 데이터 데이터베이스에 저장되는 데이터에 관한 정보이므로, 데이터에 대한 데이터를 의미해 메타 데이터라고도 한다.
시스템 데이터베이스, 사용자 데이터베이스 데이터 사전도 데이터를 저장하는 데이터베이스의 일종이기 때문에 시스템 데이터베이스라고도 한다. 사용자가 실제로 이용하는 데이터가 저장되는 일반 데이터베이스를 사용자 데이터베이스라 부르기도 한다.
데이터 사전은 DBMS가 스스로 생성하고 유지하는 것으로, DBMS가 주로 접근하지만 일반 사용자도 접근할 수 있다.
단, DBMS가 데이터를 사전에 내용을 새로 추가하거나 수정할 수 있는 반면, 사용자는 저장 내용을 검색만 할 수 있다.
데이터 사전에 있는 데이터에 실제로 접근하는 데 필요한 위치 정보는 데이터 디렉터리라는 곳에서 관리한다.
데이터 사전, 데이터 디렉터리 데이터 사전과 데이터 디렉터리는 둘 다 시스템을 위한 데이터베이스라는 공통점이 있지만, 데이터 사전은 사용자가 접근할 수 있고 데이터 디렉터리는 시스템만 접근할 수 있다는 차이가 있다.
데이터베이스 사용자
데이터베이스를 이용하는 사용자는 매우 다양한데, 이용 목적에 따라 크게 데이터베이스 관리자, 최종 사용자 응용 프로그래머로 나눌 수 있다.
데이터베이스 관리자
데이터베이스 관리자는 데이터베이스 시스템을 운영 및 관리한다.
데이터베이스를 직접 활용하기보다는 조직 내의 사용자를 위해 데이터베이스를 설계 및 구축하고, 제대로 서비스할 수 있도록 데이터베이스를 제어한다.
그래서 데이터베이스 관리자는 데이터 언어 중 주로 데이터 정의어와 데이터 제어어를 이용해 데이터베이스에 접근한다.
최종 사용자
데이터를 조작(삽입, 수정, 삭제, 검색) 하기 위해 DB에 접근하는 사람들을 일반 사용자 또는 최종 사용자라 한다.
최종 사용자는 컴퓨터 시스템이나 데이터베이스에 관한 전문 지식을 꼭 갖출 필요는 없다. 그리고 데이터 정의어를 사용할 수도 있지만 주로 데이터 조작어를 사용한다.
응용 프로그래머
응용 프로그래머는 프로그래밍 언어로 응용 프로그램을 작성할 때 데이터베이스에 접근하는 데이터 조작어를 삽입하는 사용자다.
데이터 정의어를 삽입할 수도 있지만 주로 데이터 조작어를 삽입한다.
데이터 언어
데이터베이스에 사용자를 대신해 데이터베이스를 구축하고 활용 및 관리하는 DBMS에 부탁할 때 사용하는 언어가 있다.
이것이 바로 데이터 언어다.
데이터 언어는 사용자가 데이터베이스를 구축하고 이에 접근하기 위해 DBMS와 통신하는 수단이다.
인간의 언어에 때와 장소에 맞는 용법이 있는 것처럼 데이터 언어에도 상황에 따른 용법이 있다.
데이터 언어는 DBMS의 정의, 조작, 제어 기능을 이용하기 위한 수단이기 때문에 사용 목적에 따라 데이터 정의어, 데이터 조작어, 데이터 제어어로 나뉜다.
이는 하나의 데이터 언어를 기능에 따라 내부적으로 구분 짓는 것일 뿐 독립적으로 존재하는 언어들은 아니다.
데이터 정의어(DDL)
새로운 데이터베이스를 구축하기 위해 스키마를 정의하거나 기존 스키마의 정의를 삭제 또는 수정하기 위해 사용하는 데이터 언어
데이터 정의어로 정의된 스키마는 데이터 사전에 저장되고, 삭제나 수정이 발생하면 이 내용도 데이터 사전에 반영된다.
데이터 사전에 저장된 스키마 정보는 사용자나 DBMS이 필요할 때 참고할 수 있다.
명령어
기능
CREATE
SCHEMA, DOMAIN, TABLE, VIEW, INDEX를 정의
ALTER
Table에 대한 정의를 변경하는데 사용
DROP
SCHEMA, DOMAIN, TABLE, VIEW, INDEX를 삭제
데이터 조작어(DML)
사용자가 데이터의 삽입, 삭제, 수정, 검색 등의 처리를 DBMS에 요구하기 위해 사용되는 데이터 언어
데이터 정의어를 이용해 스키마를 정의하면 스키마에 따라 조직에 필요한 실제 데이터 값(인스턴스)이 저장되는데, 사용자가 실제 데이터 값을 활용하기 위해 사용하는 것이 데이터 조작어이다.
데이터 조작어는 설명 방식에 따라 아래의 두 가지로 나눈다.
절차적 데이터 조작어 : 사용자가 어떤 데이터를 원하고 해당 데이터를 얻으려면 어떻게 처리해야하는지 구체적으로 설명
비절차적 데이터 조작어 : 사용자가 어떤 데이터를 원하는지만 설명
명령어
기능
SELECT
테이블에서 조건에 맞는 튜플을 검색
INSERT
테이블에 새로운 튜플을 삽입
DELETE
테이블에서 조건에 맞는 튜플을 삭제함
UPDATE
테이블의 조건에 맞는 튜플의 내용을 변경함
데이터 제어어(DCL)
데이터베이스에 저장된 데이터를 여러 사용자가 무결성과 일관성을 유지하며 문제없이 공유할 수 있도록, 내부적으로 필요한 규칙이나 기법을 정의하는 데 사용하는 데이터 언어다.
데이터베이스 관리자가 데이터 관리를 목적으로 사용하는 언어이다.
명령어
기능
COMMIT
데이터베이스 조작 작업이 정상적으로 완료되었음을 관리자에게 알림
ROLLBACK
데이터베이스 조작 작업이 비정상적으로 종료되었을 때 원래의 상태로 복구
GRANT
데이터베이스 사용자에게 사용 권한을 부여함
REVOKE
데이터베이스 사용자의 사용 권한을 취소함
DBMS의 구성
질의 처리기
사용자의 데이터 처리 요구를 해석하여 처리하는 역할을 담당하고, 다음의 주요 구성 요소들을 포함한다.
DDL 컴파일러 : 데이터 정의어로 작성된 스키마의 정의를 해석한다. 그리고 저장 데이터 관리자의 도움을 받아 새로운 데이터베이스를 구축하고, 스키마의 정의를 데이터 사전에 저장한다. 데이터 정의어로 작성된 기존 스키마의 삭제나 수정 요청도 처리하여, 변경된 내용을 데이터 사전에 적용한다.
DML 프리 컴파일러 : 응용 프로그램에 삽입된 데이터 조작어를 추출하여 DML 컴파일러에 전달한다. 단, 데이터 조작어와 관련 없는 나머지 코드들은 해당 언어의 컴파일러에 보내진다.
DML 컴파일러 : 데이터 조작어로 작성된 데이터의 처리(삽입 ·삭제·수정·검색) 요구를 분석하여 런타임 데이터베이스 처리기가 이해할 수 있도록 해석한다.
런타임 데이터베이스 처리기 : 저장 데이터 관리자를 통해 데이터베이스에 접근하여, DML 컴파일러로부터 전달받은 데이터 처리 요구를 데이터베이스에서 실제로 실행한다.
트랜잭션 관리자 : 데이터베이스에 접근하는 과정에서 사용자의 접근 권한이 유효한지를 검사하고, 데이터베이스 무결성을 유지하기 위한 제약조건 위반 여부를 확인한다. 회복이나 병행 수행과 관련된 작업도 담당한다.
저장 데이터 관리자
저장 데이터 관리자는 디스크에 저장된 데이터베이스와 데이터 사전을 관리하고, 여기에 실제로 접근하는 역할을 담당한다.
그런데 디스크에 저장된 데이터에 접근하는 것은 운영체제의 기본 기능이므로 저장 데이터 관리자는 운영체제의 도움을 받아 데이터베이스에 대한 접근을 수행한다.