반응형

지난 포스팅까지 TCP프로토콜을 사용하는 서버와 클라이언트를 구현해보았다.
이번 포스팅에서는 서로 연결된 네트워크끼리 데이터를 통신하는 send함수와 recv함수에 대해서 알아본다.

1) send 함수

send(socket, buffer, sizeof(buffer), 0);

send함수를 통해 소켓으로 연결되어있는 다른 프로세스에게 데이터를 보낼 수 있다.

- 1번째 인자: 데이터를 보낼 연결상태의 소켓
- 2번째 인자: 전송할 데이터를 담고있는 데이터버퍼의 포인터. 데이터버퍼는 char배열 형식이다
- 3번째 인자: 데이터 버퍼의 byte크기 
- 4번째 인자: 특별한 옵션을 주기 위한 플래그이다. 옵션은 '|| (OR)'로 조합할 수 있다. 따로 옵션을 지정하지 않는다면 0을 주어 생략가능하다.

옵션 값 옵션 값 의미
MSG_DONTROUTE 데이터를 전송할때 라우팅 테이블을 참조하지 않는다
MSG_OOB 데이터를 긴급 전송모드(Out-of-band data 모드)로 전송한다

send함수는 오류가 없다면 보냈던 데이터의 byte크기를 반환한다. 만일 오류가 발생한다면 SOCKET_ERROR라는 오류코드를 반환한다. WSAGetLastError() 함수를 호출하면 어떤 종류의 오류가 발생하였는지 확인할 수 있다.


2) recv 함수

recv(clientSocket, buffer, BUF_SIZE, 0);

recv함수를 통해 다른 프로세스로부터 온 데이터를 수신할 수 있다.

- 1번째 인자: 데이터를 수신할 연결상태의 소켓
- 2번째 인자: 수신된 데이터를 담을 데이터버퍼의 포인터. 데이터버퍼는 char배열 형식이다
- 3번째 인자: 데이터 버퍼의 byte크기
- 4번째 인자: 특별한 옵션을 주기 위한 플래그. send함수와 마찬가지로 별 옵션을 주지않는다면 0을 기입한다.

recv함수도 정상적으로 수신되었을 때에는 받은 데이터의 byte크기를 반환한다. 오류가 발생하였을때는 마찬가지로 SOCKET_ERROR를 반환한다.


이번 포스팅으로 데이터를 전송하는 send()와 recv()함수를 알아보았다.
이후 포스팅에서는 echoServer(보냈던 메세지를 그대로 답해주는 서버)를 우선 구현하여 서버와 클라이언트 통신을 다뤄볼 예정이다.

반응형

지난번 포스팅에서는 서버의 소켓을 만들었다
이번에는 클라이언트의 소켓을 정의해보고 서버에 연결한다.

1. 클라이언트의 소켓

//클라이언트 소켓 생성
SOCKET clntSocket;
clntSocket = socket(AF_INET, SOCK_STREAM, 0);

if (clntSocket == INVALID_SOCKET) {
	printf("invalid socker error");
	return 0;
}

//서버 소켓 주소정보 입력
sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servAddr.sin_port = htons(11000);


//서버로 연결
if (connect(clntSocket, (sockaddr*)& servAddr, sizeof(servAddr)) != 0) {
	printf("connect() error! [error code: %d\]n", WSAGetLastError());
	return 0;
}

클라이언트의 소켓 생성과정은 서버에 비해 훨씬 간단하다.


1) 클라이언트 소켓 생성

clntSocket = socket(AF_INET, SOCK_STREAM, 0);

socket함수를 통해 소켓을 생성한다. 소켓 생성함수의 인자들에 관한 설명은 이전 포스팅들에서 다뤘으므로 생략한다.
여기서 3번째 인자는 0을 기입해서 생략할 수 있는데, 이 경우에는 자동적으로 알맞은 프로토콜이 배정된다. 



2) 소켓 연결

connect( clientSocket, (sockaddr*)&servAddr, sizeof(servAddr))

 connect 함수를 사용하여 클라이언트에서 서버로 연결할 수 있다.

1번째 인자: 현재 다른것과 연결이 되지않은 소켓을 기입한다
2번째 인자: 연결할 정보를 담은 sockaddr 구조체의 포인터
3번째 인자: 2번째로 넘겨준 sockaddr 구조체의 byte 크기

클라이언트 소켓을 생성하고, 이것을 서버 소켓의 주소를 대상으로 연결하면 서버-클라이언트 통신이 완성된다.


서버와 클라이언트의 통신이 수립되는 과정을 요약한 그림이다.


다음 포스팅에서는 이렇게 서로 연결된 서버-클라이언트가 데이터를 송수신하는 Winsock함수를 살펴보고,
보냈던 말을 그대로 답변하는 echo서버를 구현할 것이다.

반응형

이전의 글에서 소켓을 만드는 법을 배웠다. 이제는 만든 소켓을 이용해서 두 프로세스 간의 통신을 하는 작업을 할 것이다.
여기서 TCP환경과 UDP환경에서 필요한 작업이 다르다. 그래서 이번 포스팅에서는 TCP에서의 통신을 먼저 알아보려 한다. 만들어진 소켓을 이용해서 서버가 TCP통신을 시작하는 일련의 과정들을 포스팅한다.

SOCKET servSocket, clntSocket;

//1. 소켓 생성하기
servSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

//2. 소켓 주소 구조체 만들기
sockaddr_in servAddr;
ZeroMemory( &servAddr, sizeof(servAddr) );

//3. 소켓 주소 구조체에 정보 작성하기
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servAddr.sin_port = htons(11021);

//4. 소켓에 주소 bind하기
if (  bind(servSocket, (sockaddr*)& servAddr, sizeof(servAddr)) != 0 ) {
	printf("socket bind error!");
	return;
}

//5. 소켓을 listen상태로 만들기
if ( listen(servSocket, 0) != 0 ) {
	printf("socket listen error!\n");
	return;
}

//6. 클라이언트의 연결이 오면 accept하기
int clntAddrLen = sizeof(clntAddr);
clntSocket = accept( servSocket, (sockaddr*)&clntAddr, &clntAddrLen);
if (clntSocket == -1) {
	printf("socket accept error!\n");
	return;
}

 

1) 소켓주소 구조체 정의하기

먼저 소켓을 통신하기 전에, 소켓의 주소를 할당하는 작업이 필요하다.
소켓이 연결할 주소를 저장하는 구조체를 윈도우즈 소켓 라이브러리가 제공해준다.
소켓 구조체중에 대표적인 2가지인 sockaddr구조체sockaddr_in구조체를 알아보자.

struct sockaddr {
        ushort  sa_family;
        char    sa_data[14];
};

 sockaddr은 2byte의 주소체계를 지정하는 부분과, 14byte의 주소정보를 지정하는 부분이 있다.
sockaddr 구조체의 주소 데이터를 저장하는 sa_data배열에는 ip주소(ex 127.0.0.1)과 port번호(8080), 공백문자(sockaddr구조체의 총 크기를 16byte로 맞추기위함)로 구성되어 있다. 공백문자들로 구성되어있다.

struct sockaddr_in {
        short   sin_family;
        u_short sin_port;
        struct  in_addr sin_addr;
        char    sin_zero[8];
};

struct in_addr {
    unsigned long s_addr; 
};

 반면 sockaddr_in은 2byte의 주소체계를 지정하는 부분과, 2byte의 포트를 지정하는 부분, 4byte 크기의 주소를 저장하는 in_addr구조체로 구성된 sin_addr, 그 외에 8byte의 0으로만 구성된 sin_zero필드가 구성되어있다.
sin_zero는 sockaddr_in구조체가 sockaddr와 크기를 맞추기위해 존재하지만, 아무런 의미를 가지지 않고있다. 

[출처] https://topic.alibabacloud.com/a/the-difference-between-sockaddr-and-sockaddr_in-reproduced_8_8_31409815.html

 

The difference between sockaddr and sockaddr_in (reproduced)

Original link: http://kenby.iteye.com/blog/1149001 struct sockaddr and struct sockaddr_in are two structures used to handle the addresses of network traffic. In various system calls or functions, these two structures are used whenever dealing with network

topic.alibabacloud.com

사실 이후 다루게 될 소켓에 주소를 할당하는 bind함수는 sockaddr 구조체를 사용한다. 따라서 연결할 주소정보와 포트정보가 분리된 sockaddr_in 구조체는 sockaddr정보로 변환되어 할당된다.

 

 


2) bind(): 소켓의 주소 할당하기

소켓 주소 구조체를 정의하였으면, bind함수를 통해 정의된 주소를 소켓에 할당할 수 있다.
bind 함수는 소켓에 ip주소를 할당하는 역할을 한다.

int bind(
  SOCKET          s,
  const sockaddr* addr,
  int             addrlen
);

1번째 인자: 주소를 할당할 소켓
2번째 인자: 소켓의 주소
3번째 인자: 소켓의 주소정보의 길이

이 bind 함수는 정상적으로 할당된다면 0을 반환한다. 만일 오류가 발생한다면 SOCKET_ERROR(-1 값)을 반환한다.


3) listen():  소켓을 클라이언트를 받을 수 있는 상태로 만들기

bind까지 마친 소켓은 주소를 가진 소켓이 된다. 여기에서 다른 곳에서 접속을 시도하는 클라이언트를 받을 수 있게 하려면, 해당 소켓을 접속 대기상태로 만들어줘야한다. 이는 listen함수를 이용하여 진행한다.

int WSAAPI listen(
  SOCKET s,
  int    backlog
);

1번째 인자: listen상태로 만들 소켓
2번째 인자: 대기 가능한 최대 연결 갯수. 0을 지정하면 default값으로 지정된다.

이 작업을 진행한 소켓은 이제 수동적으로 연결이 오기를 기다리게 된다. listen함수를 성공하면 0, 실패하면 -1을 반환한다. 


4) accept():  클라이언트로부터 오는 연결을 수락한다

listen상태가 된 소켓은 클라이언트로부터 연결이 오면, accept함수를 이용해 그 정보를 저장한다. 
이 함수는 연결된 클라이언트를 정보로 담은 소켓을 반환값으로 return한다. 만일 accept에 실패하면 -1을 반환한다.
accept함수는 블로킹 함수이다. 따라서 이 함수가 실행될 때 까지 해당 스레드는 대기하게 된다.

SOCKET WSAAPI accept(
  SOCKET   s,
  sockaddr* addr,
  int* addrlen
);

1번째 인자: listen 상태가 된 서버소켓
2번째 인자: 연결된 클라이언트의 주소정보를 담을 sockaddr 구조체. 연결이 되면 해당 구조체에 클라이언트 주소정보가 저장된다.
3번째 인자: 2번째 인자로 넘겨준 구조체의 크기

여기서 2번째와 3번째 인자는 옵션이므로 NULL로 생략이 가능하다.


위의 과정을 거쳐서 서버의 소켓을 생성하였다. 다음 번에는 클라이언트 프로그램에서 소켓을 생성하는 부분과, 서버와의 통신에 대해 포스팅해보겠다.

반응형

1. 소켓(Socket)의 정의

 소켓(Socket)이란 컴퓨터 네트워크/내부 프로세스 끼리의 통신(UDS를 통해 내부 통신이 가능하다)에서 데이터를 송수신되는 데이터들의 엔드포인트를 말한다. 이 엔드포인트는 IP주소와 Port주소의 조합으로 이루어져있다.

컴퓨터의 프로그램은 이러한 소켓을 생성하고, 생성된 소켓을 통해 데이터를 교환하게 된다.

조금 더 쉽게 설명하기 위해 컴퓨터 네트워크를 전화통화에 비유해보자. 통화를 하기위해선 전화가 가능한 전화기나 휴대폰이 필요하다. 소켓은 이러한 전화기/휴대폰과 비슷하며, 고유한 IP주소는 전화번호와 비슷한 개념이다.

[출처]

https://docs.oracle.com/javase/tutorial/networking/sockets/definition.html

https://ko.wikipedia.org/wiki/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC_%EC%86%8C%EC%BC%93


2. 소켓의 생성과 프로토콜

Windows에서는 아래의 소켓구조체를 정의하여 소켓을 생성할 수 있다. 

SOCKET WSAAPI socket(
  int af,
  int type,
  int protocol
);

소켓을 구성하기 위한 인자는 다음과 같다.

- 1번째 인자: 소켓이 사용할 주소 체계를 지정한다. 아래의 표는 대표적인 것을 추렸다.

주소체계 이름

설명

PF_INET / AF_INET

IPv4 인터넷 주소체계

PF_INET6 / AF_INET6

IPv6 인터넷 주소체계

PF_UNIX /AF_UNIX

UNIX 방식의 주소체계

 

 

Winsock라이브러리를 살펴보면 그 이외의 주소체계를 찾아볼 수 있다. 여기서 주의할 점은 OS 버전에 따라 지원받지 못하는 주소 체계가 존재할 수 있고, 주소 체계에 따라 socket의 type(2번째 인자값)이 제한되는 경우도 존재한다.

 

 

 

 

 

 

 

 

 

※ PF방식과 AF방식은 무슨 차이일까?

PF는 Protocol Family의 약자이고, AF는 Address Family의 약자이다. 이렇게 주소 체계와 프로토콜 체계를 따로 지정한 것은, 하나의 주소 체계가 다른 프로토콜 체계를 지원할 것을 기대하고 주소 체계와 프로토콜 체계를 나누었다. 하지만 그 의도대로의 일은 일어나지 않았고 둘의 기능은 구분이 없어졌다. 

[출처] https://beej.us/guide/bgnet/html/#system-calls-or-bust

 

Beej's Guide to Network Programming

 

beej.us

https://stackoverflow.com/questions/6729366/what-is-the-difference-between-af-inet-and-pf-inet-in-socket-programming

 

What is the difference between AF_INET and PF_INET in socket programming?

What is the difference between AF_INET and PF_INET in socket programming? I'm confused between using AF_INET and PF_INET in socket() and bind(). Also, how to give ip-address in sin_addr field?

stackoverflow.com


- 2번째 인자: 소켓에서 전송되는 데이터의 타입을 정의한다.  여기에서 1번째 인자로 지정한 주소체계에 따라, 지원되지 않는 타입도 존재할 수 있다.

소켓의 타입 종류는 아래와 같다.

소켓 타입 이름

설명

SOCK_STREAM

- TCP방식의 전송을 하는 소켓을 만든다.
- 전송되는 데이터의 경계가 없다
- 양방향으로 연결된 상태에서 데이터를 전송한다.
- 전송의 신뢰성을 보장한다(전송되는 데이터가 변조되거나 유실되지 않는다)
- 속도가 UDP방식보다는 느리다

SOCK_DGRAM

- UDP방식의 전송을 하는 소켓을 만든다
- 전송되는 데이터에 경계가 존재한다.
- 연결을 따로 성립하지 않고 출발지와 목적지 정보를 가지고 데이터를 전송한다.
- 전송의 신뢰성이 보장되지 않는다(전송되는 데이터의 변조 및 유실 가능성이 있다)
- 속도가 TCP보다 빠르다

SOCK_RAW

- 전송 프로토콜(TCP나 UDP)을 지정하지 않은 소켓
- 전송계층의 하위인 IP계층까지 접근이 가능하다
- 전송 프로토콜이 지정되지 않았기에, 헤더 정보를 사용자가 자유롭게 지정할 수 있다.

- Windows Sockets 1.1버전에서는 SOCK_STREAM과 SOCK_DGRAM만 지원하였지만 WindowsSockets 2버전부터는 보다 더 다양한 SOCK_SEQPACKET / SOCK_RDM등 보다 더 다양한 데이터 전송방식을 지원해준다.

- SOCK_STREAM과 SOCK_DGRAM이 대중적으로 많이 쓰인다. 

[출처] https://www.quora.com/Whats-the-difference-between-SOCK_RAW-and-SOCK_STREAM


- 3번째 인자: 소켓의 프로토콜을 지정한다. 

여기에서 앞서 지정한 주소체계(1번째 인자)와 데이터 전송방식(2번째 인자)에 따라 설정할 수 있는 프로토콜이 제약된다. 예를들어 2번째 인자에서 SOCK_DGRAM을 지정한 상태에서, 3번째 인자에 TCP 프로토콜을 채택하는 IPPROTO_TCP를 지정할 수 없다. 

아래는 지정할 수 있는 프로토콜의 종류이다.

프로토콜 이름

설명

IPPROTO_TCP

TCP프로토콜을 사용한다.
주소체계가 AF_INET나 AF_INET6이며,
소켓타입이 SOCK_STREAM이여야 가능하다.

IPPROTO_UDP

UDP프로토콜을 사용한다.
주소체계가 AF_INET나 AF_INET6이며,
소켓타입이 SOCK_DGRAM이여야 가능하다.

IPPROTO_ICMP

ICMP프로토콜을 사용한다.
주소체계가 AF_UNSPEC/ AF_INET /AF_INET6 중 하나이며, 소켓타입이 SOCK_RAW나 정의되어있지 않아야 가능하다.

IPPROTO_IGMP

IGMP프로토콜을 사용한다.
주소체계가 AF_UNSPEC/ AF_INET /AF_INET6 중 하나이며, 소켓타입이 SOCK_RAW나 정의되어있지 않아야 가능하다.

여기서 전송계층에 속하는 통신 규약인 TCP와 UDP는 깊게 파보면 전송되는 데이터 세그먼트 구조나, TCP UDP의 헤더정보의 차이점,
TCP가 신뢰성있는 전송을 위해 지원하는것 등 많은 부분이 있지만 분량이 많아 이후 다루어볼 생각이다.

TCP와 UDP의 간략한 차이점은 위에 SOCK_STREAM타입의 소켓과 SOCK_DGRAM타입의 소켓에 대해 기술한 차이점과 동일하다.  

한편 ICMP와 IGMP는 네트워크 계층에 속한다(전송계층보다 하위이다).
ICMP는 통신을 원하는 원격 컴퓨터가 정상적으로 연결되지 않을때,  에러상황을 보고해주는 기능을 가지고 있다. 그래서 다른 IP와 통신이 가능한지와 응답을 측정하는 Ping명령어에 사용된다.

IGMP은 멀티캐스팅(한 출발지에서 하나의 그룹으로의 통신을 지원하는 전송 유형)을 지원하는 프로토콜이다. IP-TV와 같이 한정된 대역폭에서 효율을 위해 사용된다.

 


우선 이 글을 통해 소켓을 만드는 방법과, 소켓이 사용할 주소체계와 프로토콜에 대해 알아보았다.
이렇게 만든 소켓은 이후 bind-accept-listen과 같은 작업들이 필요하며, 이를 통해 서로 다른 두 컴퓨터끼리도 통신이 가능하게 된다.

반응형

1. 회복(Recovery)

1)회복의 정의 

데이터베이스는 여러가지 요인으로 인해 데이터에 모순이 생기는 등 손상을 입는 경우가 있습니다. 이런 상황이 발생하면 DBMS에 있는 회복 관리자(recovery manager)를 통하여 데이터베이스가 손상되기 이전인 상태로 복구하는 작업을 하는데, 이것을 회복이라고 부릅니다. 회복은 대부분 로그 파일에 기록된 로그 정보를 이용해서 수행합니다. 데이터베이스의 회복이 이루어지는 동안에는 데이터베이스에 접근하여 업무를 처리할 수 없으므로 이 작업은 빠른 시간 내에 이루어져야 합니다.

로그(Log): 운영체제나 소프트웨어가 실행 중에 발생하는 이벤트나 각기 다른 사용자의 통신 소프트웨어 간의 메시지를 기록한 내역입니다. 여기서 데이터베이스에 쓰이는 로그를 트랜잭션 로그라고 하는데, 이는 데이터베이스에서 충돌이나 하드웨어 고장이 있었다고 해도 DBMS의 ACID특성을 보장하기 위해서 트랜잭션이 수행되어 데이터가 변경된 이력을 기록한 것입니다. 이런 로그 정보를 담은 파일을 로그 파일(Log File)이라 합니다.

 


 

2) 회복 기법

 - 즉시 갱신(Immediate Update) 기법: 트랜잭션 수행 중에 데이터를 변경한 연산의 결과를 데이터베이스에 즉시 반영하며, 장애 발생에 대비하기 위해 데이터 변경 사항을 로그 파일에도 기록합니다. 만일 장애가 발생하면 해당 로그의 내용을 토대로 회복 작업을 수행합니다.

 - 지연 갱신(Deferred Update) 기법: 트랜잭션의 수행 중에 데이터가 변경되어도 데이터베이스에 반영하지 않고 로그 파일에만 기록해두었다가, 부분 완료가 되면 로그에 기록된 내용을 통해 데이터베이스에 한꺼번에 반영하는 방법입니다. 트랜잭션이 수행 중 장애가 발생해도 데이터베이스에 반영된 것이 아니므로 데이터베이스는 그대로 유지되며, 로그에 기록된 내용은 파기됩니다.

 - 검사 시점(Check Point) 기법: 트랜잭션이 실행되는 중간에 일정 시간 간격으로 검사 시점(checkpoint)을 만들어두고, 해당 검사 시점까지 수행 및 완료된 내용을 데이터베이스에 적용시킵니다. 만일 장애가 발생하면 가장 최근 검사 시점 이전의 트랜잭션에는 회복 작업을 수행하지 않고, 이후의 트랜잭션에만 회복 작업을 수행합니다.

 - 그림자 페이징(Shadow Paging) 기법: 로그를 사용하지 않고선 데이터베이스를 동일한 크기의 단위인 페이지로 나눈 뒤, 각 페이지마다 복사하여 그림자 페이지를 보관하는 기법입니다. 데이터베이스의 변경되는 내용은 원본 페이지만 적용되고, 장애가 발생되면 그림자 페이지를 이용하여 회복합니다.

 


2. 병행 제어(Concurrenct Control)

1) 병행 제어란?

DBMS는 병행 수행(concurrency)기능을 지원하여 여러 개의 트랜잭션이 동시에 수행할 수 있게 만들며, 여러 사용자가 데이터베이스를 동시에 공유할 수 있도록 만들어줍니다. 병행 수행은 여러 트랜잭션들이 차례로 번갈아 수행되는 인터리빙(interleaving) 방식으로 진행되는데, 트랜잭션들이 동시에 같은 데이터에 접근하여 연산을 수행하는 경우 데이터의 모순, 갱신 분실, 연쇄 복귀 등의 문제가 생길 수 있습니다. 이러한 문제를 해결하기 위해 트랜잭션의 수행을 제어하는 것이 병행 제어입니다. 병행 제어는 동시성 제어라고도 부릅니다.

[참고] 병행 수행과 병행 제어 (데이터베이스 개론, 2013. 6. 30., 한빛아카데미(주))

 

 


2) 병행 제어 기법: 로킹(Locking)

  로킹은 병행 제어의 대표적인 기법입니다. 병행 수행도중 하나의 트랜잭션이 사용하는 데이터에 자물쇠를 걸어 다른 트랜잭션이 접근할 수 없게 만들어, 트랜잭션이 마무리 될때까지 독점해서 사용하도록 만드는 방법입니다. LOCK 명령어를 통해 독점권을 획득하고, 트랜잭션이 실행된 뒤 완료되면 UNLOCK명령어로 독점권을 해체하여 다른 트랜잭션이 데이터를 사용할 수 있게 만듭니다.

 로킹은 병행 수행에서 일어나는 위험성을 방지하지만, 하나의 데이터를 공유하는 다른 트랜잭션의 연산을 지연시키게 됩니다. 따라서 필요한 부분만 LOCK을 설정하는 것이 바람직합니다. 예를들어 2개의 트랜잭션이 동일한 데이터에게 'read연산→트랜잭션 연산→해당 데이터에게 write로 갱신' 작업을 하는 경우에는 모순이 생길 수 있어서 먼저 연산을 수행하는 트랜잭션이 read와 write 모두 LOCK을 거는것이 바람직합니다.

 하지만 2개의 트랜잭션이 모두 데이터를 단순히 읽어오기만 하는 read 연산만 한다면, 한 트랜잭션이 실행될 때 다른 트랜잭션도 데이터에 접근해서 연산을 수행해도 문제가 생기지는 않습니다. 이런 경우에는 쓰기는 허용하지 않되, 읽기는 허용하여 성능을 높일 수 있습니다.

 이렇게 로킹은 용도에 따라 공유 락배타 락으로 구분할 수 있습니다.

 

- 공유 락(Shared Lock): LOCK을 설정한 데이터에 대해 read만 허용하고, write는 불허하는 방법

 

 

- 배타적 락(Exclusive Lock): LOCK을 설정한 데이터에 대해 read과 write모두 불허하는 방법

 

 

2-1) 로킹 단위(Locking Granularity)

로킹 단위는 LOCK을 설정하는 대상의 범위입니다. 이 단위는 속성, 튜플, 테이블, 전체 데이터베이스 등 다양한 범위로 설정이 가능합니다. 여기서 로킹의 단위가 크면 제어 기법을 구현하기 편리하지만 병행성 수준이 떨어집니다. 반대로 로킹 단위가 작으면 제어 기법을 구현하기 복잡하지만 병행성의 수준이 높아집니다.

 

 

2-2) 2단계 로킹 프로토콜(Two Phase Locking Protocol)

확장 단계축소 단계라는 2개의 단계로 구성된 로킹 기법입니다. 이 기업은 트랜잭션 스케줄의 직렬성을 보장해주지만, 교착 상태(deadlock)가 발생할 수 있다는 단점이 있습니다.

 

- 확장 단계(Growing Phase): 트랜잭션들이 LOCK연산만 수행할 수 있고, UNLOCK이 불가능합니다.

- 축소 단계(Shrinking Phase): 트랜잭션들이 UNLOCK연산만 수행할 수 있고, LOCK이 불가능합니다.

 

 


3) 타임 스탬프 순서기법

 시스템에 들어오는 트랜잭션의 순서대로 고유번호인 타임 스탬프를 부여하고, 이 타임 스탬프의 순서에 따라 데이터에 접근하여 실행하는 기법입니다. 타임 스탬프 기법은 정해진 순서에 따라 실행되므로 교착상태가 발생하지 않습니다.

 

반응형

트랜잭션(Transaction)

1) 트랜잭션의 정의

 트랜잭션이란 데이터베이스 내에서 한꺼번에 모두 수행해야 할 연산들의 집합으로, 하나의 작업 처리를 위한 논리적인 단위를 뜻합니다. 이는 동시성제어(Concurrency Control)의 기본 단위기도 합니다. 트랜잭션 내의 연산은 모두 한꺼번에 완료되어야 하며, 일련의 연산을 실행하는 도중 오류가 생긴다면 모든 작업을 취소해야 합니다. 트랜잭션이 성공적으로 종류되었을 경우에는 그 결과를 데이터베이스에 영구적으로 반영하기 위해 COMMIT 명령어를 사용하고. 도중 오류가 발생했을 경우에는 수행 작업을 취소하고 이전 상태로 되돌리기 위해 ROLLBACK 명령어를 사용합니다.

 [예시] 은행에서 계좌에 입금을 하는 경우 <계좌번호 입력> → <계좌번호 정보 불러오기> → <입금할 금액 입력> → <승인> → <계좌의 잔액에 입금된 금액을 추가> 이라는 일련의 작업이 하나의 트랜잭션이 될 수 있습니다. 이 작업들이 마지막까지 성공하여  COMMIT명령어가 실행되면 영구적으로 반영되지만, 도중에 오류가 생긴다면 ROLLBACK명령을 통해 모든 작업을 취소해야 합니다.

 


2) 트랜잭션의 특성

 

 

 이 트랜잭션에는 ACID로 요약되는 4가지 중요한 특성이 있습니다.

원자성(Atomicity): 트랜잭션의 가장 중요한 특성으로, 트랜잭션 내의 연산은 반드시 모두 반영되거나 아니면 전혀 반영되지 않아야 한다는 것을 뜻합니다. 즉, 부분 실행이 허용되지 않습니다.

일관성(Consistency): 트랜잭션이 실행을 성공적으로 완료했다면, 언제나 일관성 있는 데이터베이스 상태를 유지해야하며 결과에 모순이 없어야합니다.

- 독립성(Isolation): 격리성이라고도 부르며, 하나의 트랜잭션이 수행되고 있을때 다른 트랜잭션이 접근할 수 없는 성질입니다. 즉 각각의 트랜잭션은 독립적이여야 합니다.

- 지속성(Durability): 영속성이라고도 부르며, 트랜잭션이 성공적으로 완료되면 그 결과는 영구적으로 반영되어야 합니다.

 


3) 트랜잭션의 상태도

 

트랜잭션이 수행되는 과정에는 활동, 부분완료, 완료, 실패, 철회 5가지의 상태가 있습니다. 아래는 그 상태들의 변화를 그린 상태도입니다.

 

 

 

 

 

- 활동(active): 트랜잭션이 현재 실행중인 상태

- 부분 완료(partially committed): 실행을 마치고선 데이터베이스에 결과를 저장하기 직전의 상태

- 완료(committed): 트랜잭션의 변경 내용을 성공적으로 저장한 상태

- 실패(failed): 트랜잭션 실행 중 오류가 난 상태

- 철회(aborted): ROLLBACK명령어를 통해 트랜잭션의 수행 이전상태로 돌린 상태

 


4) 트랜잭션 스케쥴

 여러 트랜잭션들이 동시에 실행되는 경우 스케쥴링을 통하여 트랜잭션의 연산들이 실행되는 순서를 정합니다. 이런 스케쥴 기법에는 직렬 스케쥴(Serial Schedule)비직렬 스케쥴(Nonserial Schedule)이 있습니다.

- 직렬 스케쥴(Serial Schedule): 각 트랜잭셜 별로 구분하여 한 트랜잭션을 구상하는 일련의 연산들이 모두 실행된다면, 다른 트랜잭션을 실행하는 방법입니다.

- 비직렬 스케쥴(Nonserial Schedule): 인터리브(Interleave) 실행기법을 통하여 각 트랜잭션의 연산들이 번갈아가며 병행 실행히는 방법입니다.

직렬 가능 스케쥴(Serializable Schedule): 직렬 스케쥴을 이용한 결과와 비직렬 스케쥴을 이용한 결과가 동일하게 나오는 경우를 말합니다.

반응형

1. 순차접근(Full Table Scan)

 데이터베이스의 한 릴레이션에서 데이터를 찾거나 재배열하기 위해 데이터가 저장된 목록 중의 모든 데이터 요소를 차례차례 조사하여 원하는 것을 찾아내는 것순차접근(Full Table Scan)이라 합니다. 이런 순차 접근에 의한 검색은 튜플의 수가 많아지면 검색 시간이 매우 오래걸린다는 단점이 있습니다. 만일 데이터가 오름차순 또는 내림차순으로 정렬되어 있다면, 이진 탐색을 통하여 검색시간을 줄일 수 있겠지만, 데이터들이 지속적인 변화를 통해 삽입,삭제,갱신 작업이 일어나므로 항상 오름차운이나 내림차순으로 유지하기에는 현실성이 떨어집니다.


 2. 인덱스(Index)

 1) 인덱스 정의, 인덱스의 장단점

 스캔기법의 단점을 보완하기 위해 등장한 것이 인덱스입니다. 인덱스는 데이터의 주소를 기억하고 관리하는 인덱스 파일(Index File)실제 데이터를 기억하는 데이터 파일(Data File)로 구성되어 있습니다. 데이터를 검색 시, 먼저 인덱스 파일에서 찾고자하는 데이터가 저장된 주소를 찾고 그 뒤 찾은 주소값을 통해 데이터 파일에서 데이터를 찾습니다. 이 인덱스 기법은 수많은 데이터 중에서 원하는 자료를 빠르게 검색할 수 있도록 해줍니다.

 하지만 인덱스는 저장공간을 차지하는 문제와 함께, 항상 정렬상태를 유지해야하기 때문에 튜플의 값이 변경되거나 수정, 삭제가 되면 인덱스도 재정렬을 해야합니다. 그래서 인덱스를 많이 사용한다면, 인덱스의 재정렬에서 많은 시간이 소요되어 DBMS의 성능을 저하하는 단점이 있습니다. 그래서 인덱스는 필요한 속성에만 사용해야합니다. 여기서 중복값을 허용하지 않는 인덱스를 고유 인덱스라고 부르는데, 이는 가장 좋은 선택성을 가집니다.

※ 선택성(=분포도, Selectivity): 전체 행의 수 분에 해당 값

 인덱스 기법은 여러가지 구조로 형성되는데 그 구조 중에선 B트리B+트리, 클러스터드 인덱스, 넌클러스터드 인덱스가 있습니다.

[예시]

[데이터 파일]

 주소값

제품코드 

제품명 

 분류

가격 

 100

AA01

탁자

가구

700,000

 200

AA02 

나무옷장

가구

400,000

 300

AA03

침대

가구

1,100,000

 400

BA01

50인치 TV 

가전

900,000

 500

BA02 

전기밥솥

가전

250,000

 600

BA03 

냉장고

가전 

1,500,000

제품의 정보들을 저장하는 데이터 파일이 있고, 왼쪽에 각 값에 해당하는 주소값이 배정되어 있다고 가정해봅니다. 이 데이터베이스의 인덱스 파일을 작성하면 아래와 같습니다.

 

[인덱스 파일]

제품코드

주소

AA01

 100

AA02

 200

AA03

 300

BA01

 400

BA02

 500

BA03

 600

이렇게 작성된 인덱스 파일을 통해 자료를 검색할 수 있습니다. 만일 제품코드 'BA01'이란 값의 데이터를 찾으려고 한다면, 먼저 인덱스 파일을 통해 해당 값이 저장된 물리적 주소인 '400'을 찾습니다. 그 뒤 데이터 파일에서 400주소값을 가지는 항목을 바로 찾아가 원하는 데이터를 찾을 수 있습니다. 만일 순차접근방식을 통해 검색한다면 AA01부터 BA01까지 모두 검색하여 더 많은 시간이 소요될것입니다.

 


 

 

 

2) 인덱스의 종류

- B―트리(Balanced Tree)

 자료 구조를 균형있는 트리구조로 나타내어 검색의 효율을 증진시키는 방법입니다. 트리의 한 노드는 다음과 같이 구성됩니다.

여기서 P1은 data1보다 작은 값을 가지는 하위 노드의 주소를 가리키며, P2는 마찬가지로 data2보다 작은 값을 가지는 하위 노드의 주소를, P3는 data3보다 작은 값을 가지는 하위노드의 주소를 가리킵니다.

[예시]

여기서 연파란색의 배경이 있는 된 표의 셀은 주소값을, 배경이 투명한 표의 셀은 데이터를 나타냅니다. 


- B+―트리

B 트리의 변형으로 단말 노드를 찾기 위한 인덱스 세트와, 단말 노드로만 구성순차 세트로 구성되어 있습니다.

[예시]

 


- 클러스티드 인덱스(Clustered Index)

 

테이블에서 하나의 속성을 기준으로 정렬시켜 재구성한뒤, 그 테이블을 토대로 인덱스를 만드는 방법입니다. 그래서 테이블의 물리적 순서와 인덱스의 순서가 동일하게 됩니다.

 

- 넌클러스티드 인덱스(Non Clustered Index)

 테이블을 재구성하지 않고, 데이터 주소를 이용하여 인덱스를 만든 뒤 주소값을 이용하여 검색하는 방법입니다.

반응형

1. 정규화(Normalization)

정규화란 논리적 데이터 모델링 단계에서 데이터의 중복과 종속으로 인한 이상 현상(anomaly)을 방지하기 위해, 속성들 간의 종속 관계를 분석하여 무결성을 유지하면서 다수의 릴레이션으로 분리하는 과정을 말합니다. 장점으로는 이상의 발생 가능성을 줄이지만, 연산 시간이 증가한다는 단점이 있습니다.

정규화를 통해 분해된 결과를 정규형이라 하며, 이 정규형의 종류는 1정규형, 제2정규형, 제3정규형, BCNF, 제4정규형, 제5정규형 등이 있습니다. 이 정규형들은 차수가 높아질 수록 제약조건이 증가된다는 특징을 갖춥니다.

 

1) 제1정규형(1NF, First Noraml Form)

한 릴레이션을 구성하는 모든 도메인이 원자값으로 된 정규형입니다.

 

2) 제2정규형(2NF, Second Normal Form)

제1정규형을 만족하면서 릴레이션에 존재하는 부분 함수적 종속을 제거하여, 모든 속성이 기본키에 완전 함수 종속이 되도록 만들어진 정규형입니다.

 

3) 제3정규형(3NF, Third Normal Form)

제2정규형을 만족하면서 릴레이션을 구성하는 속성들 간의 이행적 종속관계를 분해하여 속성들이 비이행적 함수 종속관계를 만족하도록 만들어진 정규형입니다.

 

4) 보이스-코드(BCNF, Boyce-Codd Normal Form)

제3정규형을 만족하면서 릴레이션의 모든 결정자가 후보키가 되도록 하는 정규형입니다.

 

5) 제4정규형(4NF, Fourth Normal Form)

BCNF를 만족하면서 릴레이션에서 다치 종속 관계를 제거한 정규형입니다.

 ※다치 종속(MVD, Multival Dependency): 두 속성이 1:다 대응이 되는 경우입니다.

[예시] 예를들어 '학번'과 '수강과목' 속성이 있다면, 한 학생은 여러 과목을 수강할 수 있는 경우 하나의 '학번'은 여러개의 '수강과목'에 대응하므로 다치 종속 관계입니다.

 

6) 제5정규형(5NF, Fifth Normal Form)

후보키를 통하지 않은 조인종속(Join Dependency)을 제거한 정규형입니다.

 


 

2. 역정규화(DeNormalization)

역정규화란 정규화를 통해 분리되었던 릴레이션에서 중복을 허용하고 다시 통합하거나 분할하여 구조를 재조정하는 과정입니다. 정규화된 릴레이션은 하나의 릴레이션을 분해하기 때문에 원하는 자료가 하나의 릴레이션에 존재하지 않아 외래키를 이용해 참조해야하는 상황이 잦아집니다. 이는 데이터베이스에 저장된 자료를 검색하는 시간을 증가시키며 성능을 저하시킬 수 있습니다. 따라서 데이터베이스의 물리적 설계 과정에서 성능을 향상시키기 위해 역정규화를 실행합니다. 역정규화의 종류로는 릴레이션 역정규화, 속성 역정규화가 있습니다.

 

1) 릴레이션 역정규화: 릴레이션의 역정규화에는 릴레이션을 병합하는 방법과 분할하는 방법이 있습니다.

- 릴레이션 병합: 두 릴레이션 간의 잦은 참조로 성능이 저하될 경우 이 문제점을 해결하기 위해 병합합니다.

- 릴레이션 분할: 릴레이션의 데이터를 검색할때는 목록중의 데이터를 순차적으로 조사하여 원하는 자료를 찾습니다. 그래서 자주 사용하지 않는 속성이나 튜플이 릴레이션에 있을 경우 검색시 성능을 저하하게 만듭니다. 이 경우에는 자주 사용하는 속성이나 튜플을 분해하여 성능을 향상시킵니다. 이 분할에는 수직 분할(자주 사용하는 속성과 그렇지 않는 속성을 구분해서 분할)수평 분할(자주 사용하는 튜플과 그렇지 않는 튜플을 구분해서 분할)이 있습니다.

 

2) 속성 역정규화: 릴레이션의 성능을 향상시키기 위해 속성 또는 파생속성을 추가합니다.

   ※파생 속성(Delivered Attribute): 현재 릴레이션에는 없는 속성이지만 작업의 효율을 위해 힌 속성으로부터의 계산이나 가공에 의해 파생되는 속성입니다.

반응형

함수적 종속(Functional Dependency)

 함수적 종속이란 어떤 릴레이션 R이 있을때 X와 Y를 각각 속성의 부분집합이라고 가정해봅니다. 여기서 X의 값을 알면 Y의 값을 바로 식별할 수 있고, X의 값에 Y의 값이 달라질 때, Y는 X에 함수적 종속이라고 합니다. 이 경우 X를 결정자, Y를 종속자라고 합니다. 이를 기호로 표현하면 X→Y입니다. 이런 함수적 종속관계에는 완전 함수적 종속부분 함수적 종속이행적 함수 종속이 있습니다.

[예시]

학번  이름 나이  성별  전공코드 전공명 
 110011 박지현  26 여성  AAA1 국문학과
 110011 박지현  26 여성  C0B7 컴퓨터공학과 
 131001 김민석 25  남성   C0A5 전기전자공학과
 120006 홍현희  25 여성   B1027 무용과
 150705 한태민  23  남성   C0A5 전기전가공학과 
 171024 설화영  22 여성 B01K2 공예과

이 릴레이션에서는 '학번'을 알면 '이름', '나이', '성별' 속성을 식별할 수 있으며, '힉번'이 다르면 그에따른 값도 다릅니다.
따라서 '이름', '나이', '성별' 속성은 '힉번'에 함수적인 종속관계입니다. 같은 이유로 전공 속성또한 '전공코드'에 함수적인 종속관계에 있습니다.

학번→이름, 학번→나이, 학번→성별

 


1) 완전 함수적 종속(Functional Dependency)

  완전 함수적 종속이란, 종속자가 기본키에만 종속되며, 기본키가 여러 속성으로 구성되어 있을경우 기본키를 구성하는 모든 속성이 포함된 기본키의 부분집합에 종속된 경우입니다.

[예시]

회원번호  이름 나이  거주지역
 A001 송민지 17 서울
 A002 박아람 15 부산
 A003 이예은 16 대전

이 릴레이션에서는 기본키가 '회원번호' 속성으로 구성되어 있습니다. 여기서 '이름', '나이', '거주지역' 속성은 기본키인 '회원번호'을 알아야 식별 가능합니다. 따라서 '이름', '나이', '거주지역'은 '회원번호'에 완전 함수 종속된 관계입니다.

고객ID  상품코드 주문상품 수량  가격 
AAAA01 T001 티셔츠 12000
AAAA01  B110 청바지  11000
AAAA02 B110 청바지 2 22000
AAAA03 T091 와이셔츠 1 15000
AAAA03 O100 원피스 1 19000

해당 릴레에션의 기본키는 '고객ID'와 '상품코드' 속성으로 구성되어 있습니다. 여기서 '수량' 속성은 기본키를 구성하는 '고객ID', '상품코드' 속성을 모두 알아야 식별할 수 있습니다. 따라서 '수량'은 완전 함수 종속된 관계입니다.

 


 

2) 부분 함수적 종속(Partial Functional Dependency)

부분 함수적 종속이란, 릴레이션에서 종속자가이 기본키가 아닌 다른 속성에 종속되거나, 기본키가 여러 속성으로 구성되어 있을경우 기본키를 구성하는 속성 중 일부만 종속되는 경우입니다.

[예시]

 

고객ID  제품코드 주문상품  수량  가격 
AAAA01 T001 티셔츠 12000
AAAA01  B110 청바지  11000
AAAA02 B110 청바지 2 22000
AAAA03 T091 와이셔츠 1 15000
AAAA03 O100 원피스 1 19000

기본키가 '고객ID'와 '상품코드' 속성으로 구성된 위의 릴리이션에서서 '주문상품'은 기본키 중 '상품코드'만 알아도 식별할 수 있습니다. 이 경우에는 '주문상품' 속성은 기본키에 부분 함수 종속된 관계입니다.

 

 


3) 이행적 함수 종속(Transitive Functional Dependecy)

릴레이션에서 X, Y, Z라는 3 개의 속성이 있을 때 X→Y, Y→Z 이란 종속 관계가 있을 경우, X→Z가 성립될 때 이행적 함수 종속이라고 합니다. 즉, X를 알면 Y를 알고 그를 통해 Z를 알 수 있는 경우를 말합니다.

[예시]

상품번호  상품명 소분류 대분류
 A001 검은 체크무늬 셔츠 상의 의류
 A002 유선 마우스 마우스 전자제품
 A003 유기농 양배추 야채 식료품
 A004 포테이토 칩 스낵 식료품
 A005 32인치 4K 모니터 모니터 전자제품
 A006 청바지 하의 의류
 A007 분홍 스웨터 상의 의류

이 릴레이션에서 '상품번호'를 알면 '소분류'를 알 수 있고, '소분류'을 알면 '대분류'도 알 수 있습니다. 따라서 '상품번호'를 알면 '대분류'를 알 수 있으므로 이행적 함수 종속 관계입니다.
이때, 대분류는 소분류에 의해 관계되는 항목이지만, 상품번호를 통해 귀속되어있습니다. 따라서 이런 관계를 이행적 함수 종속이라고 합니다

 

 

 

반응형

이상(Abnormal)

 릴레이션에서 일부 속성들의 종속이나 데이터의 중복으로 인해 데이터 조작시 불일치가 발생하는 것을 말합니다. 이상의 종류에는 삽입이상, 삭제이상, 갱신 이상이 있습니다.

학번

 이름

나이 

성별 

전공코드

전공명 

 110011

박지현 

26

여성

 AAA1

국문학과

 110011

박지현 

26

여성

 C0B7

컴퓨터공학과 

 131001

김민석

25 

남성 

 C0A5

전기전자공학과

 120006

홍현희 

25

여성 

 B1027

무용과

 150705

한태민 

23 

남성 

 C0A5

전기전가공학과 

 171024

설화영 

22

여성

B01K2

공예과

 

- 삽입 이상(Insert Abnormal): 자료를 삽입할때 의도하지 않은 자료까지 삽입해야만 자료를 데이터베이스에 추가하는게 가능한 현상입니다.

[예시] 데이터베이스에 '간호학과'라는 전공을 저장해야 한다면, 전공코드와 전공명만 추가하는 것이 아니라 수강하는 학생들의 정보까지 추가해야합니다.

 

- 갱신 이상(Update Abnormal): 중복된 데이터 중 일부만 수정되어 데이터의 모순이 일어난 이상입니다.

[예시] 학번이 '110011'번인 학생 '박지현'이 이름을 '박현지'로 개명했을때 모든 튜플의 정보를 변경해야 한다. 만일 국문학과 전공이 저장된 항목만 이름이 변경되고, 컴퓨터공학과 전공이 저장된 항목에선 이름이 변경되지 않았다면, 데이터의 불일치가 발생합니다.

 

- 삭제 이상(Delete Abnormal): 어떤 정보를 삭제하면, 유용한 다른 정보까지 삭제되어버리는 이상입니다.

[예시] 학번이 '120006'번인 학생 '홍현희'의 자료를 지우면 해당 릴레이션에서 저장된 전공 정보중 무용과라는 전공도 지워져버립니다.

+ Recent posts