데이터베이스 언어 (Database Language)

데이터베이스 언어란?

데이터베이스 언어(영어: Database Language)는 컴퓨터의 데이터베이스 작업을 위한 컴퓨터 언어이다. 데이터베이스 언어를 사용하여 데이터베이스 사용자 및 응용 프로그램 소프트웨어는 데이터베이스에 액세스 할 수 있다. 데이터베이스를 취급하는 기능 중 검색(질의)가 중요하기 때문에, 통례는 (데이터베이스) 쿼리 언어라고도 불린다. 그러나 데이터베이스 언어 및 질의 언어는 개념적으로 겹치는 부분도 있지만, 동의어는 아니다.

2008년 현재 가장 대중적 데이터베이스 언어는 관계 데이터베이스의 데이터베이스 언어 SQL이다.

- 위키백과 (데이터베이스 언어)

데이터베이스 언어란 데이터베이스를 다루기 위한 명령어의 모음이라고 할 수 있다.
데이터베이스를 어떠한 목적을 갖고서 다루는가에 따라서 데이터 정의 언어(DDL), 데이터 조작 언어(DML), 데이터 제어 언어(DCL) 그리고 트랜잭션 제어 언어(TCL)로 구분한다.


1) 데이터 정의 언어 (DDL; Data Definition Language)

데이터 정의 언어(영어: data definition language, DDL)는 컴퓨터 사용자 또는 응용 프로그램 소프트웨어가 컴퓨터의 데이터를 정의하는 컴퓨터 언어 또는 컴퓨터 언어 요소이다. ...(중략)...

SQL의 데이터 정의 언어의 문장은 관계형 데이터베이스의 구조를 정의한다. SQL에 의해 정의되는 관계형 데이터베이스의 구조는 쌍(행), 속성(열), 관계(테이블), 인덱스 파일 위치 등 데이터베이스 고유의 특성을 포함한다.

- 위키백과 (데이터 정의 언어)

데이터 정의 언어는 데이터베이스의 구조, 즉 테이블을 다루기 위한 명령어들이다.
데이터 정의 언어에는 CREATE, ALTER, DROP, TRUNCATE가 있다.

  1. CREATE
  • 새로운 테이블을 생성하기 위한 명령어
CREATE TABLE Employee (
    id INTEGER PRIMARY KEY,
    last_name VARCHAR(75) NOT NULL,
    first_name VARCHAR(50) NULL,
    date_of_birth DATE NULL
);

  1. ALTER
  • 주로 기존에 존재하는 테이블의 컬럼 정보를 변경하기 위한 명령어
  • 테이블명 변경도 가능
-- 컬럼 추가
ALTER TABLE Employee ADD address TEXT;

-- 컬럼 추가 (특정 컬럼 뒤에 위치하기)
ALTER  TABLE Employee ADD COLUMN mid_name VARCHAR(32) DEFAULT NULL AFTER last_name;

-- 컬럼 설정 변경
ALTER TABLE Employee MODIFY COLUMN first_name CHAR(55) NOT NULL;

-- 컬럼명 및 설정 동시에 변경 (컬럼명만 변경 가능)
ALTER TABLE Employee CHANGE COLUMN first_name 1st_name VARCHAR(32) NULL;

-- 컬럼 제거
ALTER TABLE Employee DROP COLUMN address;

-- 테이블명 변경
ALTER TABLE Employee RENAME TO Employee_new;

  1. DROP
  • 데이터베이스 또는 테이블을 삭제하기 위한 명령어
-- 테이블 제거
DROP TABLE Employee_new;

-- 데이터베이스 제거
DROP DATABASE Company;

  1. TRUNCATE
  • 테이블 자체가 아닌 테이블 내부에 있는 레코드 전체를 삭제하기 위한 명령어
TRUNCATE TABLE Employee;

2) 데이터 조작 언어 (DML; Data Manipulation Language)

데이터 조작 언어(영어: Data Manipulation Language, DML)은 데이터베이스 사용자 또는 응용 프로그램 소프트웨어가 컴퓨터 데이터베이스에 대해 데이터 검색, 등록, 삭제, 갱신을 위한 데이터베이스 언어 또는 데이터베이스 언어 요소이다.

2007년 현재 가장 대중적 데이터 조작 언어는 SQL 데이터 조작 언어이다. SQL은 관계형 데이터베이스에 대해 검색 및 업데이트 등의 데이터 조작을 위해 사용된다.

- 위키백과 (데이터 조작 언어)

데이터 조작 언어는 테이블 내에 저장되는 데이터를 관리하기 위한 명령어들이다.
데이터 조작 언어에는 INSERT, UPDATE, DELETE, SELECT가 있다.

  1. INSERT
  • 테이블에 데이터를 입력하기 위한 명령어
INSERT INTO Employee (id, last_name, first_name, date_of_birth) VALUES (1, 'Doe', 'John', DATE_FORMAT(NOW(), '%Y-%m-%d'));

  1. UPDATE
  • 테이블 내 데이터를 수정하기 위한 명령어
  • 수정하려는 테이블과 컬럼, 그리고 수정되는 값을 넣음
  • WHERE 등의 조건을 이용하여 특정 레코드의 값만 수정 가능
UPDATE Employee SET first_name = 'updated';

-- WHERE 조건 추가
UPDATE Employee SET first_name = 'Mike' WHERE id = 2;

  1. DELETE
  • 테이블 내 레코드를 삭제하기 위한 명령어
  • WHERE 등의 조건이 없으면 전체 레코드가 삭제됨
  • DDL 중 TRUNCATE와의 차이점으로, TRUNCATE는 명령어를 실행하면 작업이 자동으로 확정(AUTO COMMIT)이 되나, DELETE는 작업을 취소(ROLLBACK) 가능
-- 전체 레코드 삭제
DELETE FROM Employee;

-- WHERE 조건을 추가하여 일부 레코드만 삭제
DELETE FROM Employee WHERE id = 2;

  1. SELECT
  • 테이블에 저장된 데이터를 조회하기 위한 명령어
  • SELECT 뒤에 조회하려는 컬럼명을 입력하여 특정 컬럼만 조회 가능(전체 컬럼 조회는 애스터리스크(*)를 입력)
  • 컬럼명 입력 시 AS 키워드를 통해 별명(Alias)을 부여하여 출력되는 컬럼명 변경 가능
  • WHERE, GROUP BY, JOIN 등의 조건을 이용하여 원하는 데이터 조회 가능
-- 아무 조건이 없으므로 테이블 내 모든 레코드를 조회
SELECT * FROM Employee;

-- WHERE 조건을 통해 특정 레코드만 조회
SELECT * FROM Employee WHERE id = 1;

-- last_name 컬럼의 별명을 LN으로 지정
-- 데이터 조회 시 last_name의 컬럼명이 LN으로 출력됨
SELECT last_name AS LN FROM Employee;

3) 데이터 제어 언어 (DCL; Data Control Language)

데이터 제어 언어(영어: Data Control Language, DCL)는 데이터베이스에서 데이터에 대한 액세스를 제어하기 위한 데이터베이스 언어 또는 데이터베이스 언어 요소이다.
권한 부여(GRANT)와 박탈(REVOKE)이 있으며, 설정할 수 있는 권한으로는 연결(CONNECT), 질의(SELECT), 자료 삽입(INSERT), 갱신(UPDATE), 삭제(DELETE) 등이 있다.

- 위키백과 (데이터 제어 언어)

데이터 제어 언어는 데이터베이스의 유저를 생성하고 권한을 제어하기 위한 명령어들이다.
주요 데이터 제어 언어에는 GRANT, REVOKE가 있다.
데이터 제어 언어로 설정 가능한 권한으로는 아래와 같은 것들이 있다.

  • CONNECT : 데이터베이스에 연결할 수 있는 권한
  • SELECT : 데이터베이스의 데이터를 조회할 수 있는 권한
  • INSERT : 데이터베이스의 데이터를 등록할 수 있는 권한
  • UPDATE : 데이터베이스의 데이터를 수정할 수 있는 권한
  • DELETE : 데이터베이스의 데이터를 삭제할 수 있는 권한
  1. GRANT
  • 특정한 데이터베이스 유저에게 어떠한 작업을 수행할 권한을 부여하는 명령어
  • 모든 권한을 부여하려면 ALL PRIVILEGES를 권한 자리에 입력
  • 특정 테이블이 아닌 모든 테이블에 대해 권한을 부여하려면 애스터리스크(*)를 테이블명 자리에 입력(모든 DB, 모든 테이블이라면 둘 다 애스터리스크 입력)
GRANT SELECT,INSERT,UPDATE,DELETE ON Company.Employee TO User1;

-- Company 데이터베이스의 모든 테이블에 대하여 모든 권한 부여
GRANT ALL PRIVILEGES ON Company.* TO User1;

  1. REVOKE
  • 특정한 데이터베이스 유저에게 부여되어 있던 특정 권한을 박탈하는 명령어
  • 모든 권한을 박탈하려면 권한 자리에 ALL을 입력
REVOKE INSERT ON Company.Employee FROM User1;

-- 모든 권한 박탈하기
REVOKE ALL ON Company.Employee FROM User1;

4) 트랜잭션 제어 언어 (TCL; Transaction Control Language)

Transaction Control Language commands are used to manage transactions in the database. These are used to manage the changes made by DML-statements. It also allows statements to be grouped together into logical transactions.

- GeeksforGeeks (SQL | DDL, DML, TCL and DCL)

트랜잭션 제어 언어란 트랜잭션을 조작하기 위한 명령어들이다.
트랜잭션 제어 언어에는 COMMIT, ROLLBACK, SAVEPOINT가 있다.
트랜잭션 제어 언어를 사용하기 위해서는 먼저 트랜잭션을 시작(START TRANSACTION)해야 한다.

(트랜잭션에 대해 보다 자세한 설명을 원하시면 [DB] 데이터베이스 트랜잭션(Database Transaction) 참고 바랍니다!)

  1. COMMIT
  • 트랜잭션 수행 결과를 확정하기 위한 명령어
START TRANSACTION; -- 트랜잭션 시작
INSERT INTO Employee (id, last_name, first_name, date_of_birth) VALUES (3, 'Blue', 'Coral', DATE_FORMAT(NOW(), '%Y-%m-%d'));
UPDATE Employee SET first_name = 'Mike' WHERE id = 3;

COMMIT; -- 이후 데이터를 조회하면 추가된 데이터를 확인할 수 있다

  1. ROLLBACK
  • 트랜잭션 수행 결과를 취소하기 위한 명령어
  • 트랜잭션이 ROLLBACK으로 끝나면 데이터베이스는 트랜잭션이 시작되기 이전 상태로 되돌아감
START TRANSACTION; -- 트랜잭션 시작
INSERT INTO Employee (id, last_name, first_name, date_of_birth) VALUES (4, 'Red', 'Strong', DATE_FORMAT(NOW(), '%Y-%m-%d'));
UPDATE Employee SET first_name = 'Mike' WHERE id = 4;

ROLLBACK; -- 이후 데이터를 조회 시 트랜잭션 시작 이전 상태로 되돌아간 것을 확인할 수 있다

  1. SAVEPOINT
  • 트랜잭션 수행 결과의 일부는 남겨 두고, 일부는 취소하려는 등의 경우에 사용하는 명령어
  • ROLLBACK TO [SAVEPOINT 명칭] 명령어를 통해서 사용됨
  • SAVEPOINT 이전 지점까지의 트랜잭션 수행 결과는 남기고, 이후의 수행 결과는 ROLLBACK됨
START TRANSACTION; -- 트랜잭션 시작
INSERT INTO Employee (id, last_name, first_name, date_of_birth) VALUES (5, 'America', 'Captain', DATE_FORMAT(NOW(), '%Y-%m-%d'));

SAVEPOINT SVPT1; -- SAVEPOINT 지정

INSERT INTO Employee (id, last_name, first_name, date_of_birth) VALUES (6, 'Beans', 'Chris', DATE_FORMAT(NOW(), '%Y-%m-%d'));

ROLLBACK TO SVPT1; -- 이후 데이터를 조회 시 Captain America는 남아 있고 Chris Beans는 없어진 것을 확인할 수 있다

<참고 자료>

데이터베이스 스키마(Database Schema) feat. 데이터베이스 사상(Database Mapping)

스키마(schema)는 계획이나 도식(圖式)을 가리키는 영어 낱말 ... (중략) ...

데이터베이스 스키마는 자료를 저장하는 구조와 표현법을 정의한 것을 뜻하는 전산학 용어이다. ...(중략)...

스키마(schema, 도식)는 인공지능, 인지과학, 언어학 등에서 공통으로 사용하는 개념으로 지식을 표상하는 구조를 말한다.

- 위키백과 (스키마)


데이터베이스 스키마가 뭐지?

컴퓨터 과학에서 데이터베이스 스키마(database schema)는 데이터베이스에서 자료의 구조, 자료의 표현 방법, 자료 간의 관계를 형식 언어로 정의한 구조이다. 데이터베이스 관리 시스템(DBMS)이 주어진 설정에 따라 데이터베이스 스키마를 생성하며, 데이터베이스 사용자가 자료를 저장, 조회, 삭제, 변경할 때 DBMS는 자신이 생성한 데이터베이스 스키마를 참조하여 명령을 수행한다.

스키마는 3층 구조로 되어있다.

  • 외부 스키마(External Schema) : 프로그래머나 사용자의 입장에서 데이터베이스의 모습으로 조직의 일부분을 정의한 것
  • 개념 스키마(Conceptual Schema) : 모든 응용 시스템과 사용자들이 필요로하는 데이터를 통합한 조직 전체의 데이터베이스 구조를 논리적으로 정의한 것
  • 내부 스키마(Internal Schema) : 전체 데이터베이스의 물리적 저장 형태를 기술하는 것

데이터베이스 스키마란 데이터베이스가 어떤 구조로 구성되어 있는지를 보여주는 것이다. 데이터베이스에 대한 청사진 또는 설계 도면이라고 봐도 좋다.

데이터베이스 스키마는 각기 다른 관점을 지닌 사용자에 따라 아래와 같이 3층 구조로 나누어진다. 이 구조를 ANSI/SPARC 구조라고도 한다.

  • 외부 스키마(=서브 스키마)
  • 개념 스키마
  • 내부 스키마(=저장 스키마)

또한 3개 층으로 나누어진 데이터베이스 스키마들의 상호 변환을 위해 사상(Mapping)이라는 과정이 있다. 이는 데이터의 독립성$^*1$을 실현한다.

*1 데이터의 독립성?
데이터베이스 스키마 한 계층의 변화가 다른 계층의 변화 없이 이루어지는 것을 의미한다.

  • 외부/개념 사상(External / Conceptual Mapping)
  • 개념/내부 사상(Conceptual / Internal Mapping)


데이터베이스 스키마 알아보기

1) 외부 스키마 (External Schema)

  • 응용 프로그래머 및 일반 사용자 관점의 스키마
  • 사용자가 관심 있는 데이터베이스의 일부만 기술하고 나머지는 은폐
  • 하나의 데이터베이스 시스템은 여러 개의 응용 시스템을 지원하기에 여러 개의 외부 스키마가 존재
  • 보통 전체 데이터베이스의 한 논리적 부분이 되기에 '서브 스키마'라고도 함

2) 개념 스키마 (Conceptual Schema)

  • 기관 또는 조직 관점의 스키마
  • 물리적 저장 구조의 세부 사항은 은폐하고, 저장된 데이터의 종류 및 관계 등을 나타냄
  • 모든 응용 프로그램이나 사용자가 필요로 하는 데이터베이스 구조를 전체적이고 종합적인 관점에서 기술하므로 하나만 존재

3) 내부 스키마 (Internal Schema)

  • 시스템 프로그래머 및 시스템 설계자 관점의 스키마
  • 물리적 저장 장치 입장에서 저장 레코드 형식, 인덱스 유무, 저장 필드의 표현 방법, 저장 레코드의 물리적 순서 등 데이터가 실제로 어떻게 저장되는지 나타냄
  • 하나만 존재하며, 개념 스키마에 대한 저장 구조를 정의한 것이므로 '저장 스키마'라고도 함
  • 명칭에 '물리적'이 포함되긴 하나, 그 구조만 나타낼 뿐 실제 물리적 장치에 기록을 하는 단계는 아님

데이터베이스 사상 알아보기

1) 외부/개념 사상 (External / Conceptual Mapping)

  • 특정한 외부 스키마와 개념 스키마 사이의 대응 관계를 정의함
  • 논리적 데이터 독립성 실현
    • 개념 스키마가 달라져도 사상의 변경을 통해 외부 스키마에 영향을 주지 않음
    • ∴ 응용 프로그램 또는 사용자가 보는 뷰(View)의 변경 없이 개념 스키마 변경이 가능

2) 개념/내부 사상 (Conceptual / Internal Mapping)

  • 개념 스키마와 내부 스키마 사이의 대응 관계를 정의함
  • 물리적 데이터 독립성 실현
    • 다른 저장 매체로의 이동이나 파일 구조 변경 등의 물리적 변화에 대해 내부 스키마를 정확하게 변경시키면 개념 스키마에 영향을 미치지 않음
    • ∴ 응용 프로그램에도 영향을 미치지 않음

<참고 자료>

 

데이터베이스 정규화(Database Normalization)

데이터베이스 정규화란?

관계형 데이터베이스의 설계에서 중복을 최소화하게 데이터를 구조화하는 프로세스를 정규화(Normalization)라고 한다.

데이터베이스 정규화의 목표는 이상이 있는 관계를 재구성하여 작고 잘 조직된 관계를 생성하는 것에 있다. 일반적으로 정규화란 크고, 제대로 조직되지 않은 테이블들과 관계들을 작고 잘 조직된 테이블과 관계들로 나누는 것을 포함한다.

정규화의 목적은 하나의 테이블에서의 데이터의 삽입, 삭제, 변경이 정의된 관계들로 인하여 데이터베이스의 나머지 부분들로 전파되게 하는 것이다.

- 위키백과 (데이터베이스 정규화)

잔뜩 어질러져 있는 방을 상상해보자.
정리되지 않은 침대와 책상, 방 곳곳에 널부러져 있는 다양한 옷과 양말들, 쓰러진 행거, 어제 밤에 다 먹고서 치워 놓지 않은 비어 있는 치킨 박스와 사이다 페트병 등등...
이런 와중에 옷이나 가구를 새로 사서 방에 두려고 한다. 과연 순순히 될까?
그 전에 방 정리부터 해야 뭘 사서 들여놓던가 할 수 있을 것이다.

데이터베이스도 이와 같다.

데이터베이스 정규화란 잔뜩 어질러진 방과 같은 데이터베이스의 내부를 일정한 규칙을 통해 깔끔하게 정리정돈하는 것이다.
이렇게 정리정돈을 함으로써 데이터베이스에서 중복되는 데이터를 줄이고, 새로운 데이터의 삽입 / 갱신 / 삭제가 잘못될 위험을 없앤다(이상 현상 제거).

또한 데이터베이스의 구조를 확장할 때에도 기존의 구조를 거의 건드리지 않고 할 수 있다.
(여기서의 데이터베이스는 관계형 데이터베이스를 말한다.)


이상 현상이란?

데이터베이스의 정규화가 제대로 되지 않으면 데이터를 삽입 / 갱신 / 삭제할 때 예상과 다른 결과를 얻을 수도 있다. 이를 이상 현상(Anormaly)이라고 한다.

1) 삽입 이상 (Insert Anormaly)

대학교 학부생 목록에 신입생 '나뉴맨'의 데이터를 추가하려고 한다. 그러나 아직 수강 신청을 하지 않아 수강 과목 정보가 없다.
이러면 이 신입생의 수강 과목 값을 null로 하지 않는 이상 데이터를 추가할 수가 없다.

이런 경우를 삽입 이상이라고 한다.

2) 갱신 이상 (Update Anormaly)

학부생 중 '박이상'의 전공이 컴퓨터에서 정보통신으로 변경되었다. 따라서 '박이상'의 전공 값을 갱신하였다.
'박이상'의 레코드가 여러 개 있기 때문에, '박이상'의 전공 정보를 변경하려면 여러 개의 레코드가 함께 바뀌어야 했다.
그러나 갱신 처리 도중에 문제가 생겨 일부 레코드만 갱신이 되었다. 이로 인해 '박이상'의 전공이 무엇인지 정확히 알 수 없게 되었다.

이런 경우를 갱신 이상이라고 한다.

3) 삭제 이상 (Delete Anormaly)

'김디비'가 회계원리가 어려워서 수강 취소를 신청했다. 그래서 '김디비'의 수강 과목이 회계원리인 데이터를 삭제하려고 한다.
그런데 이렇게 하려고 보니 '김디비'의 레코드를 통째로 삭제하게 생겼다. 이러면 '김디비' 학생이 우리 학교에서 영영 사라지는데...?

이런 경우를 삭제 이상이라고 한다.


데이터베이스 정규화는 어떻게 하지?

데이터베이스 정규화는 정규화되는 정도에 따라 제 1 정규형(1NF, the first Normal Form)부터 제 6 정규형과 함께 EKNF, BCNF, ETNF, DKNF가 있다.
보통 제 3 정규형가지 만족하는 데이터베이스를 "정규화 되었다(normalized)"고 말한다.
따라서 여기서는 제 3 정규형까지만 다루겠다.

1) 제 1 정규형

제 1 정규형에서 데이터 테이블에 있는 레코드는 각각의 컬럼마다 단 한 개의 값만 가져야 한다.
'내 친구 목록' 테이블에서 '존'은 취미 2개가 함께 써있다. 이를 분리하여 아래와 같이 별도의 레코드로 만들어야 한다.

2) 제 2 정규형

제 2 정규형은 제 1 정규형을 만족하는 테이블의 컬럼들 간에 부분 함수적 종속이 없어야 한다(달리 말하면 완전 함수적 종속을 만족해야 한다).

함수적 종속이란?
수학의 함수에서 $x$에 의해 $y$가 결정이 되듯이, 데이터베이스에서 어떤 관계의 부분 집합 X와 Y가 있고, X에 의해 Y가 결정될 수 있으면 이때 Y는 X에 함수적 종속이라고 한다.
여기서 X 자체만으로 Y가 결정되면 완전 함수적 종속이라고 하고, X 내의 또 다른 부분 집합으로도 Y가 결정될 수 있으면 부분 함수적 종속이라고 한다.

제 1 정규형을 만족한 '내 친구 목록' 테이블을 보면 각 레코드는 {이름, 취미}를 통해 구분이 된다.
그런데 나이, 집 주소, 거주 국가, 거주 도시는 {이름}만 갖고도 정해질 수 있다. {이름, 취미}가 아닌 {이름}만으로도 이 속성들이 결정되므로 부분 함수적 종속이 있다.
이를 제거하기 위해 아래와 같이 테이블을 분리한다.

3) 제 3 정규형

제 3 정규형은 제 2 정규형을 만족하는 테이블의 컬럼들 간에 이행적 함수 종속이 없어야 한다.

이행적 함수 종속이란, A이면 B이고, B이면 C일 때, A는 C인 관계(A->B, B->C, A->C)인 관계가 성립하는 것이다.
즉, 데이터베이스 테이블에서 기본 키가 아닌 다른 컬럼이 또 다른 컬럼을 결정하는 경우를 말한다.

A :인간은 동물이다.
B : 동물은 죽는다.
C : 인간은 죽는다.

제 2 정규형을 만족하는 '내 친구 목록' 테이블을 보면 {이름}을 통해 집 주소를 알 수 있다.
그리고 {집 주소}를 통해 거주 국가와 거주 도시를 알 수 있다.
따라서 이행적 함수 종속이 존재하므로 이 테이블을 아래와 같이 분리한다.


역정규화(Denormalization)

역정규화는 정규화의 단계를 낮추는 것이다.

정규화를 하면 할수록 테이블이 세부적으로 나누어진다. 이를 통해 데이터의 중복이 감소하고, 각 테이블의 의미도 보다 뚜렷해지는 등의 장점이 있다.
그 대신 세분화된 테이블로 인해 한 테이블 내에서 필요한 데이터를 한 번에 불러오기가 힘들어진다. 이로 인해 JOIN 등을 통해 여러 테이블로부터 필요한 데이터를 불러와야 하는데, 이는 쿼리의 요청 처리 시간이 늘어나는 문제가 있다.

이런 이유로 정규화의 단계를 낮춰 약간의 데이터 중복을 허용함으로써 JOIN과 같이 처리 시간을 늘리는 쿼리의 사용 횟수를 줄인다. 그 결과 전체적인 시스템의 속도가 향상되는데, 이것이 역정규화의 목적이다.

주의 : 역정규화(denormalization)는 비정규화(unnormalized form)와 다른 것이다. 역정규화를 위해서는 먼저 정규화가 되어야 한다.


<참고 자료>

데이터베이스 트랜잭션(Database Transaction)

데이터베이스 트랜잭션이란?

데이터베이스 트랜잭션(Database Transaction)은 데이터베이스 관리 시스템 또는 유사한 시스템에서 상호작용의 단위이다. 여기서 유사한 시스템이란 트랜잭션이 성공과 실패가 분명하고 상호 독립적이며, 일관되고 믿을 수 있는 시스템을 의미한다.

이론적으로 데이터베이스 시스템은 각각의 트랜잭션에 대해 원자성(Atomicity), 일관성(Consistency), 독립성(Isolation), 영구성(Durability)을 보장한다. 이 성질을 첫글자를 따 ACID라 부른다. 그러나, 실제로는 성능향상을 위해 이런 특성들이 종종 완화되곤 한다.

어떤 시스템들에서는 트랜잭션들은 논리적 작업 단위(LUW, Logical Units of Work)로 불린다.
- 위키백과 (데이터베이스 트랜잭션)

중고 물품 직거래를 하는 상황을 상상해보자. 과정은 아래와 비슷할 것이다.

위 과정 중 하나라도 이루어지지 않는다면 직거래는 바로 끝(또는 PATO)날 것이다. 그리고 다시 새로운 거래 상대를 찾아 거래를 진행할 것이다.
반면에 모든 과정이 정상적으로 이루어진다면 직거래는 깔끔하게 끝날 것이다.

트랜잭션이 이렇다.

트랜잭션은 데이터베이스의 상태에 어떤 변화가 일어날 때 이 변화의 과정을 모아놓은 것이다.
단순하게는 블로그에 글을 써서 저장을 누르면 데이터베이스에 데이터가 들어가는 경우가 있을 것이고,
좀 더 복잡해지면 은행 송금처럼 보내는 사람과 받는 사람의 통장 유효 여부부터 잔액 확인 및 입·출금 처리 완료 확인까지가 한 묶음인 경우도 있을 것이다.


트랜잭션의 성질 (ACID)

트랜잭션의 성질이자 조건으로서 ACID로 표현되는 4가지가 있다.
Atomicity(원자성), Consistency(일관성), Isolation(고립성), Durability(지속성)이다.

1) Atomicity (원자성)

원자(原子, atom)는 일상적인 물질을 이루는 가장 작은 단위이다. 일상적인 물질들이 원소로 구성되어 있기 때문에, 이는 화학 반응을 통해 더 쪼갤 수 없는 단위와 동의어이다.
- 위키백과 (원자)

즉, 더 이상 쪼개어질 수 없는 성질이다(원자를 핵반응으로 더 쪼갤 수 있다고는 하는데, 여기서는 침묵하겠다).

하나의 트랜잭션은 그 자체로서 완전한 하나이다.
그 안에 아무리 많은 쿼리와 여러 과정들이 있더라도 트랜잭션에 속한 이상 하나라도 죽으면 다 같이 죽고, 모두가 살아남아야만 최종 목표(데이터베이스 조작)를 달성할 수 있다.

한 마디로 모 아니면 도, 0 아니면 1, All or Nothing이다.

2) Consistency (일관성)

자식에게 국가별 식사 예절을 가르치려는 부모가 있었다.
이 부모는 아이에게 한국식 식사 예절과 프랑스식 식사 예절을 가르쳤다.
한국에 있을 때 한국식 예절을 지키면 칭찬을 해줬고, 어기면 혼을 내거나 교정을 해줬다. 프랑스에서도 동일하게 하였다.
그 결과 아이는 한국에서는 한국식으로, 프랑스에서는 프랑스식으로 식사 예절을 잘 지키게 되었다.

자식이 있는 또 다른 부모가 있었다.
위 이야기의 부모처럼 아이에게 한국식 그리고 프랑스식 식사 예절을 가르쳤다.
그러나 아이가 식사 예절을 지키면 어느 때는 칭찬을 하고, 어느 때는 안했다. 예절을 안 지키면 어느 때는 혼을 내고, 어느 때는 내지 않았다. 프랑스에서도 동일하게 하였다.
그 결과 아이는 언제 어디서 어떤 식사 예절을 지켜야 할지 혼란스러웠고, 결국 한국과 프랑스 어딜 가든 식사 예절이 없는 아이가 되었다.

트랜잭션의 일관성은 트랜잭션이 수행이 완료된 후에도 데이터베이스가 일관된 상태를 유지하는 것을 의미한다. 이를 위해 데이터베이스 시스템의 고정적인 요소는 트랜잭션 전후 모두 동일해야 하며, 데이터베이스의 여러 가지 규칙과 제약을 지키는 유효한 데이터만 데이터베이스에 기록되어야 한다.

여기서 짚고 넘어갈 점은, 트랜잭션이 수행되는 도중에 데이터베이스의 내용이 변하면 트랜잭션의 결과는 변동 후의 데이터베이스가 아닌 트랜잭션이 시작된 순간, 즉 변동 전의 데이터베이스를 기준으로 적용된다.
변동 전 내용대로 일처리를 했는데 결과는 변동 후 내용을 기준으로 하라고 하면 어긋나는 부분이 생기기 때문이다.

각각의 트랜잭션이 시작부터 종료까지 자신에게 주어진 일관된 규칙을 따르므로, 그 결과 데이터베이스는 어긋남이 없이 일관된 상태를 유지할 수 있다.

한국식으로 식사를 시작했으면 식사를 마칠 때까지 한국식으로 먹는 것이다.
식사 도중에 마법에 걸려 장소가 갑자기 프랑스로 바뀌었으면 다음 식사부터 프랑스 식으로 하면 되는 것이다.
단, 식사 방식에 맞는 예절을 항상 지켜야 한다.

3) Isolation (고립성)

위에서 나온 식사 예절을 잘 지키는 가족을 다시 떠올려보자.
이 가족이 오랜만에 레스토랑에 외식을 하러 나왔다.
레스토랑이다보니 다른 여러 가족들도 있다.
편한 자리를 잡고 음식을 주문하여 이제 식사를 시작하려고 한다.
그런데 이때 식사 예절이 최악인 어느 가족이 이들의 테이블에 난입했다!
그리고 허락도 없이 예절 있는 가족의 음식을 집어먹으며 자리를 잔뜩 어지럽혔다.
그렇게 식사를 망쳤다.

지난번의 경험으로 인해 이번에는 칸막이가 있는 레스토랑으로 외식을 나왔다.
칸막이 덕분에 다른 사람도 보이지 않고, 방해하는 사람도 없어 아주 깔끔하게 식사를 했다.

출처_ mediamatic 홈페이지 캡처

트랜잭션의 고립성은 칸막이 있는 레스토랑에서의 식사와 같다.
식사를 하는 와중에 다른 가족들 누가 있는지 안 보이고 방해도 받지 않는 것처럼, 트랜잭션도 다른 트랜잭션들과 완전히 분리되어 자신의 할 일을 수행한다.

또한 현재 어떤 테이블에서 식사 중인 사람들이 식사를 완전히 다 마치고 자리를 뜨고 나서야 다음 사람들이 그 테이블에 가서 식사를 할 수 있듯이, 어떠한 대상에 대해 여러 트랜잭션이 작업을 해야 할 경우 그 대상에 도착한 순서대로 작업을 수행하고, 먼저 온 트랜잭션의 수행이 완료되어야 다음 트랜잭션이 작업을 시작할 수 있다.

4) Durability (지속성)

레스토랑에서 식사를 하고서 카드 결제를 하고 기분 좋게 집에 왔다.
그런데 그 레스토랑에서 전화가 왔다. 결제를 안 했단다. 뭐라고?
이야기를 들어보니 레스토랑 카운터의 컴퓨터가 잠시 먹통이 되어 껐다 켰는데, 주문 내역을 보니 나의 주문 내역은 있는데 결제 내역은 없다고 한다.
그래서 나의 카드 결제 내역을 보내서 재확인을 요청했고, 레스토랑이 카드사에 확인을 하니 기록이 있었다.

그런데 갑자기 번개가 쳐서 카드사 서버가 다운이 되었다가 몇 분 후에 복구되었다고 한다.
혹시나 레스토랑처럼 내 결제 내역이 없다고 하면 어쩌지? 전화를 해서 물어보니 다행히도 기록이 잘 남아 있었다. 그렇게 해결이 되었다.

트랜잭션의 지속성이란 각각의 트랜잭션이 수행되면 그 결과가 '영구적'으로 저장이 되는 것을 뜻한다.
여기서 '영구적'이라는 말은 데이터베이스가 있는 컴퓨터가 갑자기 꺼지든, 렉이 걸리든 상관 없이 트랜잭션의 수행 기록이 남아 있음을 의미한다.

위 이야기에서 레스토랑은 트랜잭션의 지속성이 없고, 카드사는 지속성이 있다고 볼 수 있다.


트랜잭션의 상태

트랜잭션의 작동 상태는 5가지로 구분된다.

  • 활동(Active) : 트랜잭션이 시작되어 실행 중인 상태
  • 부분 완료(Partially Commited) : 트랜잭션의 모든 과정이 완료되었고, COMMIT이 되기 직전인 상태
  • 완료(Commited) : 트랜잭션의 모든 과정이 완료되었고, COMMIT이 된 상태
  • 실패(Failed) : 트랜잭션 과정에 (단 1이라도) 오류가 발생하여 중지된 상태
  • 철회(Aborted) : 트랜잭션 오류로 인해 비정상 종료되어 ROLLBACK된 상태

트랜잭션의 연산

트랜잭션의 연산으로 COMMIT, ROLLBACK, SAVEPOINT 등이 있다. 추가로 COMMIT의 자동 수행 여부를 결정하는 AUTOCOMMIT 기능이 있다.

  • COMMIT : 트랜잭션이 제대로 수행된 후 그 결과를 확정하여 데이터베이스에 반영한다는 명령
  • ROLLBACK : 트랜잭션 수행 도중 오류가 발생하여 트랜잭션이 시작되기 전 상태로 되돌리는 명령
  • SAVEPOINT
    • 트랜잭션 과정을 여러 개로 나누어 주기 위한 명령
    • 이를 통해 트랜잭션 과정의 일부만 ROLLBACK 가능
  • AUTOCOMMIT : 각각의 SQL문을 하나의 트랜잭션으로 취급하여 매 SQL문마다 COMMIT이 되게 함
    • SET AUTOCOMMIT = OFF
      • 하나의 트랜잭션 단위로 COMMIT
    • SET AUTOCOMMIT = ON
      • 각각의 SQL문을 트랜잭션으로 취급하여 매 SQL문마다 COMMIT

(아래 예시는 MariaDB로 실행하였습니다.)
(불필요한 결과 출력은 생략하였습니다.)

1) COMMIT

MariaDB [nation]> SET autocommit = ON;
MariaDB [nation]> SELECT * FROM guests; -- 1
+----------+-------+
| guest_id | name  |
+----------+-------+
|        1 | John  |
|        2 | Jane  |
|        3 | Jean  |
|        4 | Storm |
|        5 | Beast |
+----------+-------+
MariaDB [nation]> START TRANSACTION; -- autocommit ON 상태에서 이걸 쓰면 COMMIT이나 ROLLBACK을 하기 전까지는 쿼리 반영이 되지 않음
MariaDB [nation]> INSERT INTO guests VALUE (10, 'a');
MariaDB [nation]> SELECT * FROM guests; -- 2
+----------+-------+
| guest_id | name  |
+----------+-------+
|        1 | John  |
|        2 | Jane  |
|        3 | Jean  |
|        4 | Storm |
|        5 | Beast |
|       10 | a     |
+----------+-------+
MariaDB [nation]> INSERT INTO guests VALUE (11, 'b');
MariaDB [nation]> UPDATE guests SET name='Name_Changed' WHERE name='John';
MariaDB [nation]> SELECT * FROM guests; -- 3
+----------+--------------+
| guest_id | name         |
+----------+--------------+
|        1 | Name_Changed |
|        2 | Jane         |
|        3 | Jean         |
|        4 | Storm        |
|        5 | Beast        |
|       10 | a            |
|       11 | b            |
+----------+--------------+
MariaDB [nation]> DELETE FROM guests WHERE guest_id = 11;
MariaDB [nation]> SELECT * FROM guests; -- 4
+----------+--------------+
| guest_id | name         |
+----------+--------------+
|        1 | Name_Changed |
|        2 | Jane         |
|        3 | Jean         |
|        4 | Storm        |
|        5 | Beast        |
|       10 | a            |
+----------+--------------+
MariaDB [nation]> COMMIT;
MariaDB [nation]> SELECT * FROM guests; -- 5
+----------+--------------+
| guest_id | name         |
+----------+--------------+
|        1 | Name_Changed |
|        2 | Jane         |
|        3 | Jean         |
|        4 | Storm        |
|        5 | Beast        |
|       10 | a            |
+----------+--------------+

START TRANSACTION으로 트랜잭션을 시작하여 COMMIT으로 끝을 내어 트랜잭션의 결과가 데이터베이스에 적용이 되었다.
(※ 참고 : autocommit을 OFF로 설정하면 START TRANSACTION을 쓰지 않아도 각 쿼리문들의 실행 결과가 COMMIT나 ROLLBACK을 하기 전까지 반영되지 않는다.)

2) ROLLBACK

MariaDB [nation]> SET autocommit = ON;
MariaDB [nation]> SELECT * FROM guests; -- 1
+----------+-------+
| guest_id | name  |
+----------+-------+
|        1 | John  |
|        2 | Jane  |
|        3 | Jean  |
|        4 | Storm |
|        5 | Beast |
+----------+-------+
MariaDB [nation]> START TRANSACTION;
MariaDB [nation]> INSERT INTO guests VALUE (10, 'a');
MariaDB [nation]> SELECT * FROM guests; -- 2
+----------+-------+
| guest_id | name  |
+----------+-------+
|        1 | John  |
|        2 | Jane  |
|        3 | Jean  |
|        4 | Storm |
|        5 | Beast |
|       10 | a     |
+----------+-------+
MariaDB [nation]> INSERT INTO guests VALUE (11, 'b');
MariaDB [nation]> UPDATE guests SET name='Name_Changed' WHERE name='John';
MariaDB [nation]> SELECT * FROM guests; -- 3
+----------+--------------+
| guest_id | name         |
+----------+--------------+
|        1 | Name_Changed |
|        2 | Jane         |
|        3 | Jean         |
|        4 | Storm        |
|        5 | Beast        |
|       10 | a            |
|       11 | b            |
+----------+--------------+
MariaDB [nation]> DELETE FROM guests WHERE guest_id = 11;
MariaDB [nation]> SELECT * FROM guests; -- 4
+----------+--------------+
| guest_id | name         |
+----------+--------------+
|        1 | Name_Changed |
|        2 | Jane         |
|        3 | Jean         |
|        4 | Storm        |
|        5 | Beast        |
|       10 | a            |
+----------+--------------+
MariaDB [nation]> ROLLBACK;
MariaDB [nation]> SELECT * FROM guests; -- 5
+----------+-------+
| guest_id | name  |
+----------+-------+
|        1 | John  |
|        2 | Jane  |
|        3 | Jean  |
|        4 | Storm |
|        5 | Beast |
+----------+-------+

START TRANSACTION으로 트랜잭션을 시작하여 ROLLBACK으로 끝을 냈다. 그 결과 그 사이에 있던 모든 쿼리들에 대한 실행 결과가 트랜잭션의 시작 전으로 되돌아갔다.

3) SAVEPOINT

MariaDB [nation]> SELECT * FROM guests; -- 1
+----------+-------+
| guest_id | name  |
+----------+-------+
|        1 | John  |
|        2 | Jane  |
|        3 | Jean  |
|        4 | Storm |
|        5 | Beast |
+----------+-------+
MariaDB [nation]> START TRANSACTION;
MariaDB [nation]> INSERT INTO guests VALUE (10, 'a');
MariaDB [nation]> SELECT * FROM guests; -- 2
+----------+-------+
| guest_id | name  |
+----------+-------+
|        1 | John  |
|        2 | Jane  |
|        3 | Jean  |
|        4 | Storm |
|        5 | Beast |
|       10 | a     |
+----------+-------+
MariaDB [nation]> SAVEPOINT two; -- 이 지점 이전으로 상태를 되돌릴 예정
MariaDB [nation]> INSERT INTO guests VALUE (11, 'b');
MariaDB [nation]> UPDATE guests SET name='Name_Changed' WHERE name='John';
MariaDB [nation]> SELECT * FROM guests; -- 3
+----------+--------------+
| guest_id | name         |
+----------+--------------+
|        1 | Name_Changed |
|        2 | Jane         |
|        3 | Jean         |
|        4 | Storm        |
|        5 | Beast        |
|       10 | a            |
|       11 | b            |
+----------+--------------+
MariaDB [nation]> DELETE FROM guests WHERE guest_id = 11;
MariaDB [nation]> SELECT * FROM guests; -- 4
+----------+--------------+
| guest_id | name         |
+----------+--------------+
|        1 | Name_Changed |
|        2 | Jane         |
|        3 | Jean         |
|        4 | Storm        |
|        5 | Beast        |
|       10 | a            |
+----------+--------------+
MariaDB [nation]> ROLLBACK TO two;
MariaDB [nation]> SELECT * FROM guests; -- 5
+----------+-------+
| guest_id | name  |
+----------+-------+
|        1 | John  |
|        2 | Jane  |
|        3 | Jean  |
|        4 | Storm |
|        5 | Beast |
|       10 | a     |
+----------+-------+

SAVEPOINT two를 설정 후, ROLLBACK을 해당 시점으로 지정하여 실행하였다. 그 결과 SAVEPOINT two 이전에 수행되었던 쿼리의 결과는 남고, 그 이후의 쿼리 결과들은 사라진 것을 확인할 수 있다.

4) AUTOCOMMIT

SET autocommit = ON;
MariaDB [nation]> SELECT * FROM guests; -- 1
+----------+-------+
| guest_id | name  |
+----------+-------+
|        1 | John  |
|        2 | Jane  |
|        3 | Jean  |
|        4 | Storm |
|        5 | Beast |
+----------+-------+
MariaDB [nation]> INSERT INTO guests VALUE (10, 'a');
MariaDB [nation]> INSERT INTO guests VALUE (11, 'b');
MariaDB [nation]> UPDATE guests SET name='Name_Changed' WHERE name='John';
MariaDB [nation]> SELECT * FROM guests; -- 2
+----------+--------------+
| guest_id | name         |
+----------+--------------+
|        1 | Name_Changed |
|        2 | Jane         |
|        3 | Jean         |
|        4 | Storm        |
|        5 | Beast        |
|       10 | a            |
|       11 | b            |
+----------+--------------+
MariaDB [nation]> ROLLBACK;
MariaDB [nation]> SELECT * FROM guests; -- 3
+----------+--------------+
| guest_id | name         |
+----------+--------------+
|        1 | Name_Changed |
|        2 | Jane         |
|        3 | Jean         |
|        4 | Storm        |
|        5 | Beast        |
|       10 | a            |
|       11 | b            |
+----------+--------------+

<참고 자료>

데이터베이스 관계(Database Relationships)

데이터베이스 관계란?

관계는 테이블 간에 둘 다 존재한다. 이 관계들은 일대일, 일대다, 다대다, 이렇게 세 가지 형태로 이루어진다. 대부분의 관계형 데이터베이스들은 각 로우의 각 컬럼이 하나의 값만을 보유할 수 있도록 설계되어 있다. (값은 원자적이다)
- 위키백과(관계형 데이터베이스 - 키 (Key) - 관계 (Relationships)

데이터베이스 관계란 데이터베이스 내 어떤 테이블들이 서로 어떻게 연결되어 있는지 나타낸 것이다.

데이터베이스 관계에는 일대일(1:1), 일대다(1:N), 다대다(N:N) 관계가 있다.

1. 일대일 관계 (One-to-one)

두 개의 테이블 A와 B가 있으면 A의 레코드 하나가 B의 레코드 하나와 연결된 관계이다.
예를 들면 한 사람의 주민등록번호와 여권 번호 같은 경우가 있다.

일대일 관계는 애초부터 하나의 테이블로 표현하는 경우가 많다.

2. 일대다 관계 (One-to-many)

테이블 A의 레코드 하나가 B의 레코드 여러 개와 연결된 관계이다.
예를 들면 한 사람이 여러 채의 아파트를 가진 경우가 있다.

3. 다대다 관계 (Many-to-many)

테이블 A의 레코드 하나가 B의 레코드 여러 개와,
테이블 B의 레코드 하나가 A의 레코드 여러 개와 연결된 관계이다.
예를 들면 대형마트의 고객과 판매 물품 간의 관계를 들 수 있다.

다대다 관계를 위처럼 표현할 수는 있으나, 실제로는 구현할 수가 없다.
만약 구현하려고 하면 한 고객의 구매 물품 번호에 여러 개의 값을 넣어야 할 수도 있는데, 이는 데이터베이스 제1 정규화를 깨뜨린다.

이런 이유로 실제로는 두 테이블 사이를 연결해주는 별도의 테이블$^*$$^1$을 만들어서 다대다 관계를 두 개의 일대다 관계로 풀어준다.

*1 이 별도의 테이블을 교차 엔티티라고 한다.


<참고 자료>

+ Recent posts