빅데이터 기반 분석은 많은 엔터프라이즈에서 기본적으로 고려하는 부분이 되었으며, 이에 대해 기본적으로 이해하는데 도움이 되는 몇가지 기본 지식들을 정리해보았다.

 

최근의 데이터 분석의 추세는 Data Gravity 라는 용어로 시작할 수 있을 것 같다. 기술적인 용어라기 보다는 추세를 비유하는 용어에 가까운데, 이 개념은 다음과 같다

 

  • 개념 : 데이터 규모가 커지고 무거워질 수록 비즈니스를 끌어당기는 중력(Gravity) 이 강해지는 현상을 나타내는 개념
  • 성질 : 데이터를 저장할 스토리지, 데이터를 운용 및 관리할 인력, 데이터 활용을 위한 어플리케이션 등 비즈니스는 데이터에 기반해서 함께 이동한다

즉, 데이터 기반의 의사 결정을 통해 서비스의 방향이 결정되고 향후 비즈니스 가치를 판단하는 기준이 바뀔 수 있다는 측면이다.

이러한 데이터 기반의 의사 결정을 위해 데이터를 다루는 큰 두가지 틀이 있을 수 있다.

 

(1) Data Warehouse (데이터 웨어하우스)

  • 데이터를 저장해두는 창고. 데이터 분석이 필요할 때 창고의 데이터를 가져다 이용하자는 개념
  • 분석을 위해 OLAP 툴 또는 SQL 을 이용하여 최종 정보 이용자들이 활용한다
  • 데이터들에 대해 정형화된 형식으로 통합해 단일 형식으로 구성
  • 복잡한 데이터 모델에 대해 갖고 있는 데이터 모델에 제약을 맞춰야하는 이유 등으로 인해 데이터 모델 통합이 어렵고, 인프라 증설에 따른 운영 비용이 많이 발생

 

데이터 웨어하우스는 잘 구성된 데이터를 규격화된 데이터베이스에 적재해놓고 쿼리를 조합해서 필요한 정보를 얻어내는 방식이다.

가장 친숙하게는 관계형 데이터베이스(RDB) 에서 쿼리를 통해 데이터를 조회하는 것도 웨어하우싱 방식의 접근 방식이라 할 수 있겠다.

 

장점으로는 적재(Storing)된 데이터를 정해진 Case 에 대해서는 다루기가 아주 쉽다. 정형화된 형식이기 때문에 마케팅, 사업 등 엔지니어가 아니라도 쉽게 조회 및 집계 등이 가능하다.

단점으로는 데이터를 잘 구성해서 적재해주는 측면이 중요하다. 특히 최근에 생성되는 비정형 데이터를 사용할 수 있는 형태의 정형 데이터로 바꿔줘야 한다. 또한 데이터가 적재되는 방식은 데이터가 사용되는 방식과 다른 경우가 많아 확장성을 갖추기가 쉽지 않다.

 

 

(2) Data Lake (데이터 레이크)

  • 정형 데이터로 구성된 전통적 소스 외에도 수많은 비정형 데이터들을 실시간으로 수집, 정제, 통합하여 활용하기 위해 확장된 개념
  • Raw 데이터 형식으로 저장했다가 나중에 쉽게 분석할 수 있도록 구성
    • 이를 위해 분산 시스템 환경에서 데이터를 독립적으로 쪼개고 다시 취합하는 기법을 사용
    • 하둡과 같은 맵리듀스 방식이 대표적
  • 데이터를 구축하고 활용하는 기술이 어려워서 습득과 활용이 쉽지 않다

데이터 레이크는 데이터 웨어하우스에서 접근 방식을 바꾼 방법이다. 비정형 데이터를 정형화시키지 않고 Raw 데이터 형태로 적재하고, 사용할 때 필요에 맞게끔 구성하는 방법이다.

 

장점으로는 데이터의 수집(Ingestion) 및 적재(Storing) 에 있어서 부담을 덜 수 있다. 데이터의 정형화에 대한 부담이 적기 때문에 새로이 비즈니스 확장이나 새로운 데이터가 들어오더라도 비정형으로 그냥 적재하면 된다. 따라서 데이터의 수집 자체가 더 용이하다.

단점으로는 결국 데이터는 사용 시에 Use Case 에 맞게 형변환되어야 한다. 따라서 적재된 데이터의 사용을 위해 별도의 인프라 및 시스템이 필요한 경우가 많다. 이런 측면으로 인해 비개발자가 다루기가 쉽지 않다.

 

 

최근의 추세는 Data Lake 형태로 온갖 데이터를 몰아넣고, 이를 적절한 사례에 맞게 가져가 쓰는 것으로 보인다.

비즈니스 확장성이 중요해진 추세다보니, 언제 어떤 데이터가 어떻게 쓰일지 모르고, 따라서 일단 적재해놓고 적합한 사용 사례에 맞게 데이터 분석 시스템을 모두 갖춰놓고, 이용하는 방식으로 많이 사용되어지는 것 같다.

이제 빅데이터도 어느새 알아야하는 기본 지식이 되어가기 때문에, 기반 지식들을 알아두면 많이 도움이 될 것으로 보인다.

 

 

구글 Protobuf 는 강력한 데이터구조이며 환경간 이식성이 뛰어나고 패킷으로 사용할 때, 자체의 성능(통신 속도 및 작은 패킷 크기)이 뛰어나지만, 정해진 형식이 있다보니 사용에 있어서 알아두어야할 점들이 몇가지 있다.

 

실무에서 사용하면서 알아두어야 했던 점들 및 특징적인 점들 몇가지를 정리해보았다.


1. protobuf 는 패킷간 상속과 추상화를 지원하지않는다.

프로토버프를 도입하기 가장 꺼려지는 이유인데, protobuf 의 Data Structure는 상속과 추상화와 같은 생산성을 위한 개발자들의 구조를 부정한다.

Protobuf 는 애초에 범용(?) 목적으로 설계되었다기 보다는 확실한 단일 목적에 대해 Compact 하게 설계된 Data Structure 이다.

즉, 확실히 의미를 갖는 필요한 데이터만 저장할 수 있게 되어있으며, 그렇기 때문에 Stream 으로 사용할 시 필요한 정보만 주고받는 효율성을 이점으로 취할 수 있는 반면, 데이터를 담는 데 있어서 유연하지 못하다.

 

그렇기 때문에 프로토콜 버퍼를 패킷으로 통신을 해야 한다면 모든 패킷에 필요한 정보들을 분류해서 정의해주는 것이 필요하다.

 


2. protobuf 의 패킷 넘버링은 생각보다 엄격하지않다. 하지만 패킷의 순서는 매우 중요하다.

다음과 같은 에러 처리를 위한 프로토콜 버퍼 Data Structure가 있다고 가정해보자.

위의 데이터 구조에서 time_stamp 패킷의 넘버링을 4로 바꿔도 무방하다. 프로토콜 버퍼에서 구조 내에서 중요한 것은 패킷들의 순서를 지키는 일이다.

다만, Protocol Buffer 를 enum 형태로 정의해서 쓰는 경우라면 조금 얘기가 다른데,

위와 같은 Enum 패킷에서는 numbering 이 의미를 갖는다. enum 의 경우 넘버링이 Enum 클래스의 ordinal 과 동치되기 때문에, Compile 해서 사용할 경우 패킷이 다르다면 예기치 못한 에러가 발생할 수 있다.

 

 

3. Protobuf 구조가 프로토콜로 정해졌다면 데이터 구조는 변경하지 않는 것이 좋다.

통신에 있어서 Protobuf 를 사용하는데, 기존 데이터 구조가 삭제 또는 변경된다면 그 구조를 삭제 & 변경하는 것이 아니라 새로 패킷을 추가하는 편이 좋다.

그 이유는 하위호환성 때문으로, 통신하는 양쪽의 Protobuf 빌드가 항상 동기화가 완벽하다면 문제가 없지만, 개발을 하다보면 버전이 달라질 수밖에 없다.

이 때 문제를 방지하기 위해 데이터 구조의 크기를 늘리는 방법을 선택해야 한다.

하지만 패킷의 크기가 커질지는 염려하지 않아도 된다. Protobuf 의 특징 상 입력되지않는 패킷은 보내지않도록 퍼포먼스 측면에서 최적화가 지원된다.

 


4. 구글이 지원해주는 protobuf 라이브러리가 있으며, 이를 사용하면 프로토버프의 사용이 매우 편리해진다. 

다음 링크를 참조하자.

https://github.com/protocolbuffers/protobuf

 

protocolbuffers/protobuf

Protocol Buffers - Google's data interchange format - protocolbuffers/protobuf

github.com

protobuf 라이브러리는 언어별로 필요한 기능들을 유틸리티 형태로 지원한다.

아주 방대하니 전체를 쓸 생각보다는 환경에 맞게 필요한 만큼만 모듈을 가져다 쓰는 것이 좋다.

또한 protobuf 라이브러리에는 proto 파일 내에서 사용할 수 있는 공통 protobuf 형식들도 정의되어 있으므로 참고하는 것이 좋다. 
가령 proto 파일 내에서 Collection 이나 timestamp 등의 기능을 사용할 수 있도록 정리되어있다. 

 

 

5. 당연한 얘기지만 proto 파일의 주석조차 compile 결과로 빌드된 소스에 포함 된다. 

만약 소스 자체로 어떤 스크립트가 실행되어야 한다면 환경에 주의하자.

주석에 한글 특수문자가 포함된 proto 파일을 자동화 스크립트로 돌리다가 문제가 생기는 일이 더러 있었다.

 

 

프로토콜 버퍼는 구글이 지원하고 있는 직렬화방식이고 강력하며 쓰임새도 다양하다.

하지만 현재로써는 아는만큼 사용할 수 있는 도구임이 분명하다. 사용에 있어 유의하고 항상 공식 지원을 참고하는 것이 좋겠다.



Protocol Buffer 는 구글이 만든 언어 및 플랫폼 중립적이고 확장성을 갖춘 새로운 형태의 직렬화 매커니즘이자 데이터 포맷이다. 

 

XML과 유사하지만 더 작고 빠르며, 포맷을 정의하기만 하면 컴파일을 통해서 C++ / C# / Java / Python / Go / Ruby 등의 언어에 대해서 바로 코드형태로 바꿀 수 있다. 


프로토버프의 파일 포맷은 .proto 이며 이 안에 자료 구조를 정의하면 프로토버프 모듈을 통해 각 언어에 맞게 컴파일을 한 뒤 사용하는 방식이다.

언어에 무관하게 포맷만 서로 공유가 되면 각 언어에 맞는 모듈들이 알아서 Serialize / Deserialize 를 해주기 때문에 쉽게 사용할 수 있다.


.proto 의 샘플 데이터 포맷은 다음과 같다.


syntax = "proto2";
option java_package = "com.model.protobuf";

message SimpleProtoBufMessage {
required int32 id = 1;
required string message = 2;
enum MessageType {
PING = 0;
REQUEST = 1;
RESPONSE = 2;
}
optional MessageType messageType = 3 [default = PING];
}


 syntax 는 컴파일할 대상 protobuf 버전을 지칭하며 java_package 와 같은 것들은 option 으로 지정되어 자바로 컴파일 시에만 적용이 된다.

위의 message 는 클래스로 매핑이 되며 그 안의 멤버변수들은 1, 2, 3 과 같은 연속된 숫자가 값으로 할당되는데 이는 직렬화 순서를 의미한다.


protobuf 2 버전과 3 버전에는 몇가지 문법에 있어서 차이가 있다.

예를 들어 위의 예제 양식에서 required 는 proto3에서 없어졌고, optional 은 proto3에서 기본형이라 역시 사라졌다. 

Default 값 지정도 proto3 에서 사라진 내용으로, protobuf 의 현 방향은 기본값을 배제함으로써 null 체크에 대해 좀 더 엄격해진 모습이라고 이해하면 된다.


위의 파일을 proto3 버전으로 바꾼다면 다음과 같이 된다.


syntax = "proto3";
package PB.Simple;
option java_package = "com.model.protobuf";

message SimpleProtoBufMessage {
int32 id = 1;
string message = 2;
enum MessageType {
PING = 0;
REQUEST = 1;
RESPONSE = 2;
}
MessageType messageType = 3;
}



.proto 를 컴파일 하기 위해서는 protobuf 모듈을 OS에 맞게 다운받아서 protoc 모듈을 이용하면 된다.


위와 같이 생성된 .proto 파일을 protoc 모듈을 이용해서 각 언어별로 컴파일을 수행하면, 다소 복잡한 형태의 코드를 얻을 수 있으며 구글에서는 최대한 이 파일을 변형하지 않고 사용하길 권장한다.


<proto 컴파일된 Class 파일 예시>



위처럼 생성된 파일은 해당 언어에서 바로 모듈 혹은 클래스의 형태로 이용이 가능하다. 


대게 프로젝트에서는 직접 이용하기 보다는 별도의 Converter 를 만들어두고, 통신시 혹은 직렬화시에만 Convert 해서 사용한다.


프로젝트 Protobuf 로 통신은 application/x-google-protobuf 와 같은 새로운 형식이며 서버 클라이언트간 통신 시 이 형태로 consume 및 produce 가 일어나야 한다.

이때, 서버와 클라이언트는 동일한 .proto 파일을 공유하기만 하면 각기 환경에 알맞게 컴파일해서 내부 클래스로 사용하면 된다. 

주로 REST 서버로 POST 에서 RequestBody 에 protobuf 모델이 담겨서 통신하게 된다.


통신에 있어서 특징적인 점은, Protobuf 통신시에 값이 default 값과 같다면 통신 시 전송하지 않는다는 점이다. 

이는 프로토버프가 프로토콜 설계시 성능을 크게 고려했다는 점을 알 수 있는 부분이다.


실무에서 사용 결과 JSON 보다 확실히 우월한 퍼포먼스를 내는 것을 실감하고 있다. Json 에 비하자면 아직은 쓰기 불편하지만, 익혀둘만한 기술임은 틀림없다.




YAML 은 JSON이나 XML과 마찬가지로 설정 파일 등의 목적을 가진 데이터를 기술하기 위해 만든 포맷으로 JSON과 비교하자면 좀 더 구조가 복잡하지만 사람이 보기에 가독성 측면에서 좀 더 자연스러운 포맷이다. 


YAML 은 E-mail 양식에서 아이디어를 얻어 읽기 쉬운 데이터 포맷으로 고안되었으며 문서 Markup Language 가 아닌 데이터 표현을 위한 가벼운 형태의 언어이다.


JSON과는 다르게 자료형을 정의하게 되어있으며 기본은 primitive 형이다. 

Scalar 라고 하는 숫자 / String 형태, Sequence 라 하는 배열/리스트 형태, Mapping 이라 하는 key-value pair 형태 등이 존재하며 #은 주석이다. 

Space를 이용한 들여쓰기로 구조체를 구분한다. (Tab이 아니다.)


하지만 프로그래밍 언어처럼 반드시 문서에 형을 명시해야하는 것은 아니고, 다음과 같이 분류만 해주면 된다.


integer : 100

string : "100"

float : 100.0

boolean : Yes


다음은 간단히 만들어본 YAML 파일 예시이다. (에디터의 사소한 플러그인 에러로 인해 보이는게 이상하지만 신경쓰지말자 ;;)


---
# List of pets
pets:
- dog
- cat
- bird
- fish

# pets in shop (list inline)
shops: ["dog", "cat"]

# Dictionary of owner's pet (hashtable inline)
map: { james: dog, jenny: cat }

# Dictionary of pet's owner
dog:
- james,
- tom,
- kate
cat:
- jenny

pageNo : 10
valid : Yes



YAML 은 몇가지 지시자를 사용하여 특정 작업들을 수행할 수 있다.

가령 %YAML 문자는 문서의 YAML 버전을, %TAG 지시자는 URI 주소를 나타낸다.


근래에 많은 오픈소스들의 YAML 을 활용하고 있고, 커뮤니케이션 포맷으로도 사용되는 것 같다. 알아두는 것이 좋을 것 같다.





ORM(Object Relational Model)은 사물을 추상화시켜 이해하려는 OOP적 사고방식과 DataModel을 정형화하여 관리하려는 RDB 사이를 연결할 계층의 역할로 제시된 패러다임으로 RDB의 모델을 OOP에 Entity 형태로 투영시키는 방식을 사용한다. 


이는 시스템에 따라, 사용하는 Database 및 DB Connector 에 따라 달라질 수 있는 데이터 매핑 구조를 객체지향형태로 통일시켜, SQL 구조의 Database를 OOP 구조의 형태로 매핑시키려는 패러다임이다.


OOP 적 구조와 SQL 구조의 차이는 데이터를 다루는 방법에서 나타난다. OOP 적 구조에서 모든 데이터는 객체이며, 각 객체는 독립된 데이터와 독립된 함수를 지닌다. 


반면 SQL 에서 데이터는 테이블단위로 관리되며 객체들을 조회하기 위한 명령어를 사용한다.

ORM 은 각 테이블 또는 구분하고자하는 데이터 단위로 객체를 구현하고, 데이터간의 관계를 형성한다. 가령 개인 정보 저장을 위한 RDB의 테이블 Personal 은 다음과 같은 객체 Personal 로 매핑될 수 있다.



이러한 ORM을 구현하는 대표적인 프레임워크가 Hibernate이며, 이를 Java 표준 방식으로 정의한 것이 JPA이다. 실무에서 개발을 할 때에는 JPA 와 Hibernate 를 같이 사용한다.


위와 같은 매핑을 ORM 을 제공하는 프레임워크들은 매우 간단한 방식으로 지원한다. 가령 JPA와 Hibernate 와 같은 Java 의 ORM 들은 Annotation 을 이용해서 간단한 매핑이 가능하다.

이러한 중간 계층을 Persistence Layer라 하며, ORM 프레임워크 들은 기존의 Entity Bean이 수행하던 Persistence Bridge 의 역할을 ORM 패러다임의 Entity 방식으로 가능하게끔 한다. 


적용되는 기준은 Data Entity와 Object 모델이 Getter와 Setter 그리고 Repository를 이용한 표준 형식으로 정의되며, RDB에서 표현할 수 있는 Join 관계 및 참조 관계, Native 쿼리 등 거의 모든 부분의 매핑이 가능하다.


ORM 은 데이터를 다루는 새로운 관점을 제시하지만 적용하는 데 있어 몇가지 고려해야할 장단점을 갖고 있다. 이는 ORM이 흔히 겪는 RDB와 OOP 사이의 아직 풀지 못한 숙제들을 말하며 다음과 같다.


1. 세밀함의 불일치(RDB 데이터 타입은 Vendor마다 다르며, 더이상 정규화가 힘들다.)


2. 하위 타입 문제 (RDB에는 상속의 개념이 없다.)


3. 동일성 문제(Java의 경우 Equals로 평가 가능하지만, DB는 PK 기준으로 한다.)


4. 연관 관계 – FK는 연관의 방향성을 갖지 못하기 때문에 N:N 모델의 경우 Link 테이블을 도입해야 한다.


5. 데이터 검색 – RDB는 한번에 많이 가져올지(메모리) 빈번하게 데이터를 가져올지(성능) 고민해야 한다.


위의 장단점은 표준 ORM 패러다임이 기존 시스템에 비해 갖는 괴리감 및 과제들이며, 많은 프레임워크들이 위의 숙제를 해결하기 위한 기능들을 제공한다.


하지만 무엇보다도 ORM 을 도입하는 데 있어서 중요하게 생각해야 할 점은 ORM 은 결코 RDB를 개발자로부터 분리시키는 기술이 아니라, 오히려 개발자에게 높은 RDB 지식을 요한다는 사실이다.

개발자는 객체 구조의 설계 시에도 Database 의 스키마를 고려해야하는 숙제를 갖게 되는 것이다.


이는 진입 장벽이 높아지는 단점으로 여겨질 수도 있으나 오히려 숙련된 개발자가 있다면, DB와 코드를 좀 더 직관적으로 연결하고 다룰 수 있게 하는 기술이 될 수 있다.


ORM 의 개념은 아직 우리나라에서는 익숙하지 않다. 아직 많은 개발자들이 SQL Mapper로 대표되는 MyBatis를 선호하는 추세이지만 전세계적으론 Application Service 기술 동향에 있어서 데이터를 다루는 기술의 하나로 각광받고 있다.



* 향후 포스팅의 카테고리를 옮길 예정입니다.


 ACID는 Atomicity, Consistency, Isolation, Durability 의 약자로 Transaction이 안전하게 수행된다는 것을 보장하기 위한 성질을 가리키는 약어이다.


트랜잭션의 처리는 Index 의 업데이트 와 같은 신경써주어야 하는 많은 변화를 수반하기 때문에 복잡한 문제이다.

ACID 원칙은 복잡하고 에러 발생 가능성이 높은 트랜잭션 상황에서도 반드시 보장해 줄 수 있는 Database 가 트랜잭션을 지원한다면 가져야 하는 성질이다.


Atomicity : 원자성은 Transaction과 관련된 작업들이 부분적으로 실행되다가 중단되지 않는 것을 보장하는 능력이다. 원자성은 중간 단계까지 실행되다가 실패하는 일이 없도록 하는 것이다. All or Nothing. 즉, 모든 작업이 성공하거나 실패한다.


- Consistency : 일관성은 Transaction이 실행을 성공적으로 완료하면 언제나 일관성있는 Valid 한 DB 상태를 유지하는 것을 의미한다. 여기서 Valid 한 상태는 트랜잭션의 결과로 업데이트된 데이터가 각종 Constraints 및 Rule 을 위반하지 않는 것을 의미한다. 무결성 제약에 위반하는 Transaction은 중단된다.


- Isolation : 고립성은 Transaction을 수행 시 다른 Transaction의 연산 작업이 끼어들지 못하도록 보장하는 것을 의미한다. 이는 Transaction 밖에 있는 어떤 연산도 중간 단계의 데이터를 볼 수 없음을 의미한다. 또한 고립성은 Transaction 실행 내역이 연속적이어야함을 의미하고, 유연성있는 제약조건이다.


- Durability : 지속성은 성공적으로 수행된 Transaction은 영원히 반영되어야 함을 의미한다. System 문제나 DB 일관성 체크 등을 하더라도 유지되어야 하고, 모든 Transaction은 로그로 남긴 채로 Rollback도 가능하다. 

Transaction은 로그에 모든 것이 저장된 후에만 Commit 상태로 간주한다.


Database는 트랜잭션에 연관된 모든 연산들을 한번에 실행하기 쉽지 않다. 

서로 강하게 결합되어 있는 연산들의 정렬 기준이 복잡하기 때문이다. 이 문제를 해결하기 위하여 DB는 연산의 처리 시 로깅을 이용하여 모든 작업에 대한 변경사항을 로그로 기록한다. 


ACID 를 보장하기 위한 방법으로 처리하는 데이터에 대해 Lock을 걸어서 관리하거나 MVCC라 하여 데이터의 복사본을 만들어두어 동시 처리 후 충돌을 후처리 하는 방안이 있다.





 처음 학부생 때 공부를 위해서 혹은 실무에서 관계형 데이터 베이스를 접하고, 직접 설계하는 일은 쉽지 않다.

많은 DB 테이블들이 직관적으로 나타나있고 관계 역시 이해하기는 어렵지 않지만 이를 구성하고자 한다면 성능, 데이터의 정합성, 중복 배제 등 고려해야할 내용은 많다. 물론 그걸 다 잘하기는 매우 힘들며, 비즈니스 특성에 따라 트레이드 오프를 감안하여 작성하게 된다. ^^;;

 처음 DB 설계를 해보려는 이들에게 이번 정리 내용의 포스팅은 큰 도움이 될 수 있다.


 * RDB 설계 시작하기 5가지 단계


(1) DB의 목적을 명확히 한다. (Define the purpose of the DB)


(2) 수집한 데이터를 적절히 나눌 수 있는 기준이 될 Primary Key 를 구성한다.


(3) 테이블 간의 관계를 구상한다. 가장 쉬운 관계의 형성은 PK를 중심으로 생각하는 것이다. 


- One to many : Parent 테이블의 모든 value는 많은 Child table의 Record를 가질 수 있으나 Child table의 모든 Value는 Parent table에서 단 하나의 Record에 대응되어야 한다.


- Many to many : Many to many의 관계는 일반적으로 다수의 one to many 관계들로 이루어질 수 있어야 한다.


- One to one : 주로 동등한 레벨의 정보에 대한 Column 분리 용도로 사용된다.


(4) Refine and Normalize the Design(정규화)


 Column의 추가나 Optional data 처리를 위한 table 분리 등.


- 1NF : 기본적으로 필요한 속성들을 하나의 entity로 모아놓고 하나의 속성을 UID로 선정했다면 0NF 상태이고, 여기서 Repeating group을 제거하면 1차 정규화 형식이라 한다.


- 2NF : 속성들이 PK의 일부에 대해서만 종속적이라면 이 Part key dependency를 제거하여 2NF 형식이 된다.


- 3NF : 2NF에서 Key가 아닌 다른 속성에 종속적일 때 Inter-data dependency라 하며 이를 제거하면 3NF 형식이 된다.


- BCNF : Key 내의 속성이 key 내의 다른 속성에 종속적이라면 Inter-key dependency라 하며 제거하면 BCNF 형식이 된다.


(5) Denormalize : 순수히 성능만을 위한 기법이다.

- Comined table : 자주 조인하는 테이블의 경우 조인 성능부하를 없애기 위해 미리 Join된 형태로 table에 합침. 그럴 수 없는 경우 Join overhead는 Index나 SQL Plan을 조정한다.


- Derived data : 계산해서 얻을 수 있는 값을 굳이 칼럼으로 따로 빼준다.


- Artificial key : 적절한 PK가 없거나 인덱싱이 힘든 경우 간단한 AK를 만들어 해결하고 PK column은 일반속성화 한다. (예를들어 굳이 나뉘어져있지 않은 기수 번호를 인공적으로 Key로 만들어주어 부여하고 이를 인덱싱하여 성능을 개선한다.)


- Denormalization 의 예시. 유저 정보 테이블 id, name, address. 유저 성적 테이블 id, score. 두 테이블의 조인이 빈번하다면 두 테이블을 합칠 수 있음. Id, name, address, score.



 * 추가적으로 좀 더 자세한 설명이 필요하다면 다음 링크가 정말 잘 정리되어있다.  http://www.ntu.edu.sg/home/ehchua/programming/sql/relational_database_design.html






data에 대한 데이터를 말하는 것으로, 다른 데이터를 설명해준다.


 정보를 효율적으로 찾기 위해서 일정한 규칙에 따라 컨텐츠에 부여되는 데이터이다.


 데이터 자체를 좀더 적은 정보량으로 표현하기 위하여 만들어졌으며, 이는 인터넷에서 문서들을 찾기 위한 일종의 태그 역할을 수행하기도 한다.


 즉, 메타데이터를 기준으로 문서를 구분할 수 있는 일종의 Unique 한 Key 역할을 할 수 있으며 이로 인해 주로 분석이나 분류 목적으로 따로 쓰이기도 한다.


실무에서도 주로 서버를 구성할 때, 관리하는 데이터는 이 meta data가 되며, meta data를 이용한 참조 혹은 인덱스로 실제 데이터를 가리키는 데 사용한다.


메타 데이터의 쓰임새는 워낙 무궁무진하며 혹자는 메타 데이터에 대한 설계 능력이 data를 설계하는 중요한 역량이라고 말하기도 하더라.


* 실무에서도 많이 쓰이는 용어이기 때문에 간단하지만 알아둘 필요가 있다.




 RDB에서 관계를 맺는데 있어서 식별관계(Identifying Relationship)와 비식별관계(Non-Identifying Reltationship)가 존재한다.

정확히는 RDBMS에서 나누는 관계가 아닌 ER Diagram 상에서 논리상 나누는 개념이며... 굉장히 헷갈린다.


 Identifying Relationship 은 부모 테이블의 기본키 또는 복합키가 자식 테이블의 기본키 또는 복합키의 구성원으로 전이되며, 관계는 서로 종속되게 된다.


 예를 들어서 B라는 테이블의 FK child A 테이블의 PK를 참조한다면 Identifying Relationship에서 B테이블은 A 테이블에 종속이 되어서 A값이 없으면 B값은 홀로 의미를 갖지 못하는 관계가 된다. 아래의 테이블을 보자


<학생 정보를 기록하는 TableA 와 학생 성적을 관리하는 TableB>


위의 테이블에서 tableA는 학생들의 정보를 기록하는 테이블이고, tableB는 학생들의 과목별 성적을 기록하는 테이블이 된다.

TableA와 TableB는 똑같이 studentId를 PK로 갖고 있으며, TableB 에서 studentId 가 없다면 TableB는 독립적으로 존재할 수 없는 정보가 된다. 이때, 우리는 학생의 성적은 학생에게 종속되어 있다는 사실을 기반으로 생각하기 때문이다. 이러한 Strong coupling 에 대한 관계가 Identifying Relationship 이다. 


 반면에 Non-Identifying Relationship 은 자식 테이블의 일반 속성(Attribute) 그룹의 구성원으로 전이되는 비식별관계로부모는 자식의 부분적인 정보만을 표현함을 의미한다위의 예에서 Non-Identifying Relationship 의 경우 B테이블의 값은 A FK의 관계를 맺고 있긴 하지만 독립적으로 존재할 수 있어야 한다.(즉, FK가 B테이블의 PK가 되어선 안된다.) 이제 위의 테이블을 Non-identifying 한 관계로 바꾸어보자.


<학생 정보를 기록하는 TableA 와 학생 성적을 관리하는 TableB>


변경된 테이블에서 TableB는 더이상 studentId를 키로 갖지 않는 대신 별도의 독자적인 subjectId를 키로 가진다. 그리고 학생정보를 기록하는 TableA가 이를 참조하는 방식으로 변경되었다. TableB의 subjectId는 특정 성적의 기록이 되며, 성적의 기록을 모아둔 성적 기록부가 된다. 학생기록부(TableA)에서 성적기록 태그(subjectId)를 찾으면 이제 해당 학생의 성적 정보가 완성된다. 즉, 두 테이블은 서로가 없이도 유효한 정보를 나타낼 수 있다.


 이해를 돕기 위해 좀 더 편한 예시를 들어보자, 가령 책과 독자와 같은 정보의 경우는 Non-Identifying 관계이며, 독자가 없어도 책은 존재할 수가 있다. 하지만 저자와 책의 경우는 저자가 없는 책이 있을 수 없으므로 Identifying 관계라고 할 수 있다.



 * 관련되어 헷갈린 개념으로 Optional 과 Mandatory라는 개념이 있다. 이는 Non-identifying Relationship에 적용되는 내용으로, 참조하는 Foreign Key가 Null이 될 수 있는가에 대한 내용이다. 가령 위의 예시에서 어떤 학생이 어떤 과목의 시험성적도 갖고 있지 않다면 subjectId는 NULL이 될 수 있다. 학생의 성적 기록이 없기 때문이다. 이 때를 Optional 하다고 하며, NULL이 허용이 안되는 상황, 모든 학생이 입학시험은 적어도 봐야한다고 하면 subjectId는 NULL이 될 수 없다. 이 때를 Mandatory 하다고 한다.


서두에 언급했듯이 이는 ERD 를 그릴때 고려되는 개념이기 때문에 어떻게보면 추상적인 개념이라고 할 수 있다. 실무에서 ERD를 그릴 때 모른다면 실수로 잘못된 커플링을 갖거나 NULL이 허용되는 상황에서 NULL이 허용되지않는 관계가 생겨날 수도 있기 때문에 알아둘법한 지식이다.




+ Recent posts