본문 바로가기

데이터베이스(DA, AA, TA)/NoSQL

[NoSQL] NoSQL 시작하기

NoSQL 개요


NoSQL은 다양한 데이터 구조를 갖습니다. 그리고 Key/Value, Column Family, Graph, Document 등 다양한 형태의 데이터베이스로 분류됩니다. Column Family의 기본 형식은 KV(Key/Value)이기 때문에 KV(Key/Value) 구조를 간단히 설명하고 넘어가겠습니다.


전통적인 데이터베이스 데이터 관리 방법은 하나의 row에 해당 row에 종속되는 속성들을 나열해 관리했습니다.일반적으로 관리되는 데이터가 NoSQL에서는 어떤 형식으로 관리되는지 사례를 들어 설명하겠습니다.



1. KV(Key/Value) Store

NoSQL에서 가장 기본이 되는 것은 Key/Value Store입니다. NoSQL을 이해하는데 있어 Key/Value 모델이 가장 기본적인 개념이고 모든 NoSQL 데이터베이스가 이 기본을 바탕으로 확장되었습니다. Key/Value Store는 단어가 내포하는 의미 그대로 key와 value만 존재합니다.


* 관계형 모델


* KV(Key-Value) 모델


속성을 간단히 key와 value만 존재합니다. NoSQL에서 키는 관계형 데이터베이스의 PK(Primary Key, 주키)가 정의되어 들어가고 속성값들이 value에 데이터로 통합되어 저장되는 무척 단순한 구조입니다. 이 구조는 아마존 DynamoDB 등에서 사용하는 형식입니다. 



2. 칼럼 패밀리(Column Family) 기본 모델


* 함유원소를 코드화한 모델(관계형 모델)


기존 관계형 데이터베이스로 구현하는 시스템에서는 위와 같은 구조를 많이 사용해 왔습니다. 특히 많은 컬럼이 요구하는 업무에서는 업무에 맞는 코드를 만들어 사용하고 있습니다. 장점은 새로운 성분 분석이 추가될 때 테이블 변경 없이도 함유원소만 데이터로 추가하면 운영이 가능하다는 점입니다.


NoSQL에서는 이러한 데이터 모델의 개념을 도입하여 데이터 모델을 확장한 것입니다. 유연성을 적극 활용해 유사한 컬럼뿐만 아니라 컬럼명이 columns이라는 속성에 데이터로 저장되고 value 속성에는 해당 컬럼의 값만 존재합니다. 예를 들어 관계형 데이터베이스에서 성별이라는 컬럼은 columns에 "성별"이라는 데이터로 저장되고 value에 "남"이라는 데이터가 저장됩니다. 이렇게 구현했을 경우 컬럼의 유연성이 확보되어 데이터 저장에 매우 큰 장점을 확보할 수 있습니다. 뒤에 NoSQL 데이터 모델 시 자세히 설명되겠지만 이 컬럼의 유연성은 기존에 관계형 데이터베이스에서는 설계하지 못하던 부분까지 설계가 가능한 혁신적인 기법입니다. NoSQL은 이러한 장점들을 적극 반영한 매우 발전된 모델입니다.


Key/Value 모델은 NoSQL에서 가장 기본적인 모델이고 Column Family 모델은 Key/Value 모델의 확장입니다. 위의 예에서 보듯이 전통적인 관계형 모델에서는 데이터를 정규화해서 위와 같이 칼럼을 수평적으로 배열했었습니다. 그러나 NoSQL 모델에서는 데이터를 수평적으로 나열해 놓는 방식이 아니고 아래로 수직적으로 쌓아가는 방식입니다. 즉 관계형 모델에서 유연성을 극대화한 모델이라고 볼 수 있습니다.


* 칼럼 패밀리(Column Family) 기본 모델


위에서 보는 바와 같이 관계형 모델에서는 테이블 생성 시 컬럼명을 정의해 줘야 합니다. 그리고 새로운 컬럼이 추가될 때는 컬럼을 추가해 테이블 구조를 바꾸어야 하고, 특수한 경우에는 데이터도 새로 적재해야 합니다. 즉 기존 데이터의 새로 추가된 컬럼에 업데이트하는 과정이 필요합니다. 즉 위에서 새로운 기혼여부를 관리할 필요가 생기면 관계형 데이터베이스에서는 테이블에 새로운 컬럼을 추가해 테이블 구조를 변경하고 미혼, 기혼여부의 데이터를 확보해 기존의 사원정보에 업데이트해야 합니다. 그러나 Column Family 테이블에서는 구조변경이 필요없고 확보된 데이터를 컬럼에 기혼여부로 하여 입력하면 됩니다. 


* Column Family 컬럼 추가


신규 데이터를 Column Family 테[이블에 입력하면 됩니다. 구조를 변경할 필요가 없고 기존 데이터를 업데이트할 필요도 없습니다. 그리고 기혼이고 자녀가 있을 경우에 자녀수가 필요하면 자녀수라는 데이터를 columns 속성으로 해당 데이터를 입력하기만 하면 됩니다.


그러나 관계형 데이터베이스에서는 자녀라는 칼럼이 없으면 먼저 자녀수 컬럼을 만들어야 하고 자녀가 있으면 자녀 수를 입력하고 자녀가 없더라도 그냥 빈 컬럼으로 존재합니다.


Column Family DB가 사용하기에 더 유연합니다. 그러나 Column Family DB의 단점은 편한 대신 타 테이블과의 조인이 안된다는 점입니다. 관계형 데이터베이스의 최대 장점인 SQL 문으로 타 테이블과 조인하여 다양한 정보를 추출할 수 있다는 점인 반면 NoSQL DB는 이 처리가 불가능한 것이 단점입니다. 그래서 NoSQL 데이터베이스에서는 조인이 필요한 데이터는 하나의 테이블에 중복으로 관리해 처리 속도를 향상 시킵니다. 동일한 데이터들이 여러 테이블에 존재하게 되는데 이것을 관리하는 것이 NoSQL 데이터베이스에서는 가장 어려운 점이고 가장 중요한 특징입니다. 


* RDB 모델 자녀 수에 Null 포함


* 컬럼 패밀리(Column Family) 모델


NoSQL에서는 관계형 데이터베이스와 다르게 컬럼의 정의가 자유롭고 컬럼이 데이터로 NoSQL 데이터베이스에 입력 저장됩니다. 관계형 데이터베이스에서는 테이블 생성시 정의되어 고정된 컬럼때문에 데이터의 값이 없을 경우에도 NULL로 관리되었으나 NoSQL에서는 데이터가 존재하지 않으면 데이터를 입력할 필요가 없습니다. 즉 디스크 공간을 효율적으로 사용할 수 있고 필요 없는 정보를 관리하지 않아도 되는 장점이 있습니다.



데이터 모델 노테이션


NoSQL Column Family 노테이션은 아직 정형화된 기법이 없을뿐더러 상용화된 모델링 툴도 찾기 힘듭니다.


(1) 기본 모델

NoSQL Column Family 기본 모델은 아래와 같이 표시합니다. 기본적인 형식이며 전형적인 Column Family 모델입니다.


* Column Family 모델


상위에 테이블명이 존재하고 다음 칸에 row key가 들어갑니다. Column Family 모델에서는 row key에 의해 모든 것이 처리되므로 row key의 정의는 무척 중요합니다. 그리고 column이 그 다음 칸에 연속해 들어갑니다. 이 column들은 테이블에 고정된 column으로 정의되는 것이 아니고 Column Family 테이블의 column field에 데이터로 들어가는 것이므로 CQ(Column Qualifier)라고 합니다.


* Column Family 기본형 모델 예(사원 테이블)


위는 Column Family 노테이션의 가장 간단한 표기법입니다. 테이블명은 사원 테이블이고 row key는 사번이고 사원의 속성은 이름, 주소, 부소가 됩니다. 이 모델은 ER 모델과 모양은 비솟합니다. 논리 모델은 이렇게 설계하고 실제 데이터베이스에 들어가는 형식은 column들이 CQ(Column Qualifier)로 데이터로 들어가 저장됩니다.


실제 데이터베이스에 저장되는 모습을 보면 아래와 같습니다.




(2) 복합 row key

위 모델은 row key가 하나인 가장 간단한 모델입니다. 그러면 row key가 하나 이상인 모델에 대해 알아보겠습니다.


* Column Family 모델의 row key 확장


row key가 두개인 경우입니다. 위에서 보는 거소가 같이 row key가 Key attr.1, Key attr.2 두개로 구성되어 있습니다. 이와 같이 row key를 두 개 이상으로 조합해 구성할 수 있습니다.


* row key 확장 구조 예시


위에서 보듯이 row key가 두 개가 됩니다. 실 데이터 저장방법을 보면 아래와 같습니다.


* row key 확장 데이터 예시


NoSQL 노테이션에서는 row key가 두 개이지만 실제로 테이블에는 한 개의 row key만 들어갑니다. NoSQL에서는 이런 복합키인 경우 두 컬럼을 한 컬럼으로 구성합니다. row key의 설계는 뒤에서 자세히 설명합니다.



(3) nested 모델

Column Family 모델은 테이블 안에 테이블을 넣을 수 있습니다. 반복된 데이터를 한 테이블로 구현할 수 있습니다. 노테이션은 아래와 같습니다.


* nested 모델 노테이션



(4) Dynamic 모델

Column Family 모델은 컬럼을 미리 정의하지 않고 데이터 발생 시 정의해 사용할 수 있습니다. 예를 들어 컬럼명이 확정되지 않을 경우에도 신규 컬럼 발생시 그 시점에 CQ에 정의해서 사용할 수 있습니다. 이런 경우에는 컬럼명이 유동적이기 때문에 모델에서는 미정의 컬럼으로 정의하고 데이터 발생 시 컬럼명이 정의됩니다. 기존 관계형 데이터베이스에서도 미정의 컬럼으로 미리 정의해 두고 사용하는 경우도 있습니다. 그러나 관계형 데이터베이스에서는 미정의 컬럼을 테이블 생성 시 미리 정의해 놓고 차우에 사용합니다.


* Dynamic 모델 노테이션



(5) Column Family 모델 노테이션

Column Family 모델은 유사한 속성을 가진 칼럼들을 그룹으로 만들어 관리할 수 있습니다. 다음은 Column Family 유형의 모델의 노테이션을 보여줍니다.


* Column Family 유형 노테이션


위에는 기 설명한 row key가 나타나있습니다. row key 아래로 Column Family가 정의되어 있습니다. CF는 Column Family의 약자입니다. 이 표현은 HBase의 정의이고 Cassandra에서는 SC(Super Column)이라고 합니다. 관계형 모델에서 마스터 테이블에 1:(1,0)로 연결되어 있는 모델은 이 SC 모델로 표현이 가능합니다. 그러므로 한 테이블에 상당히 많은 속성들이 포함될 수 있습니다. 


* Column Family 유형 노테이션의 예


위에서는 사원정보를 CF 구조로 나타내 보았습니다. Column Family로는 공통사항과 최종학력, 전화번호로 구분했습니다. 즉 공통사항, 최종학력, 전화번호가 그룹되어 관리될 수 있습니다.



(6) Column Family 응용 노테이션

지금까지 설명한 노테이션이 기본 노테이션이고 이 기본 노테이션의 조합으로 다양한 설계구조를 나타낼 수 있습니다. NoSQL 모델은 이렇듯 다양한 형태로 나타납니다.



NoSQL의 종류


NoSQL은 다양한 종류의 데이터베이스가 존재합니다. 가장 기본이 되는 Key/Value Store와 Key/Value Store에서 컬럼 개념을 확장하여 생성된 Column Family, 문서 저장에 적합한 Document Store 그리고 네트워크 구조인 Graph 구조에 적용하는 Graph 데이터베이스가 있습니다. 각 데이터베이스별로 각각의 특성이 있고 사용 용도가 다릅니다. 


1. KV(Key/Value) Store

NoSQL의 가장 기본적인 구조입니다. 여기서 store라고 하는 것은 DBMS가 아니라 단순히 데이터를 저장하는 store라는 의미에서 기존의 DBMS와의 다른 개념이고 일반적인 파일과 같이 저장하는 저장매체여서 단순히 store라고 이름을 붙였습니다. Key/Value Store는 단순히 key와 value만 갖습니다.


대표적으로 사용하는 Key/Value Store는 아마존 DynamoDB 같은 NoSQL입니다. DynamoDB는 테이블 생성을 아마존에서 제공하는 화면에서 직접생성합니다. Table Name과 Primary Key를 입력하고 테이블을 생성하는 절차를 거치면 우너하는 테이블이 생성됩니다.



그리고 데이터 입력은 다음과 같이 합니다.

static String productCatalogTableName = "ProductCatalog";
try {
	// Add book. Map<String, AttributeValue>
	item = new HashMap<String, AttributeValue>();
	item.put("Id", new AttributeValue().withN("101"));
	item.put("Title", new AttributeValue().withS("Book 101 Title"));
	item.put("ISBN", new AttributeValue().withS("111-1111111111"));
	item.put("Authors", new AttributeValue().withS().withSS(Arrays.asList("Author1")));
	item.put("Price", new AttributeValue().withN("2");
	item.put("Dimensions", new AttributeValue().withS("8.5 x 11.0 x 0.5"));
	item.put("PageCount", new AttributeValue().withN("500"));
	item.put("InPublication", new AttributeValue().withN("1"));
	item.put("ProductCategory", new AttributeValue().withS("Book"));
	PutItemRequestitemRequest = new PutItemRequest().withTableName(tableName.withItem(item);
	client.putItem(itemRequest);
	item.clear();
	// Array Sample
	item.put("Authors", new AttributeValue().withSS(Arrays.asList("Author1", "Author2")));
}

Key/Value는 간단히 key에 해당하는 value를 저장하는 단순한 저장소입니다.



2. Column Family

Column Family 모델은 Key/Value 모델이 한단계 발전한 모델입니다. 관계형 데이터베이스에서는 테이블 생성시 칼럼이 정의되어 테이블이 생성되나 NoSQL에서는 테이블 생성시에는 테이블에 정의되지 않고 테이블에 데이터가 입력될 때 컬럼도 데이터로 테이블에 저장되는 개념입니다. 이 개념은 ER 모델에서도 갖고 있지 않은 개념입니다. 


관계형 모델에서는 2레벨 구조적 속성관리가 불가능합니다. 하나의 테이블로 생성 시에는 모두 동일한 레벨에서 테이블이 생성됩니다. 이것을 구분하기 위해 일반적으로 컬럼명에 구분할 수 있게 명명규칙을 만들어 사용합니다.


NoSQL에서는 조인을 하지 않게 설계해야 하므로 하나의 테이블로 설계합니다. 다음은 사원 테이블을 NoSQL로 설계한 데이터 모델을 보여줍니다.


* NoSQL 사원 테이블


NoSQL에서는 위와 같이 간단히 구조적 모델이 해결됩니다. HBase에서는 Column Family라고 하는데 Cassandra에서는 슈퍼컬럼(Super Column)이라고 합니다. 모두 동일한 개념입니다. Cassandra에서는 테이블을 Column Family라고 합니다. NoSQL에서의 용어를 간단히 정리하면 다음과 같습니다.


1) 테이블

관계형 모델의 테이블과 동일한 개념이다.


2) Row

관계형 모델에서의 튜플(Tuple)에 해당하는 개념이다. 관계형 모델에서는 row 형태로 데이터베이스에 저장되지만 NoSQL 모델에서는 논리적으로 설계하기 위한 모델이고 실제 저장되는 단위는 아니다.


3) Column Family

이미 설명했듯이 관계형 모델에서는 존재하지 않는 구조체 모델로 관계형 모델의 서브셋 개념과 유사하다. 관계형 모델에서는 한 테이블에서는 구분이 불가능하지만 NoSQL 모델에서는 특히 Column Family 모델에서는 이 모델이 지원된다.


4) Cell

관계형 모델에서 한단계 더 내려간 개념이다. 관계형 모델에서는 집합론에서 튜플인 row 단위로 데이터베이스에 저장되므로 row까지 관리한다. 그러나 NoSQL 모델에서는 row를 컬럼 단위로 다시 분해해 저장하는데 일 value 단위를 cell이라고 한다.



관계형 모델에서는 한 줄씩 하나의 row로 저장됩니다. 즉 데이터가 row 단위로 관리됩니다. 이 모델을 Column Family 모델로 바꾸면 각 한줄이 cell이 됩니다.



RDB와 HBase, Cassandra의 용어가 각각 다릅니다. 이를 정리하면 아래와 같습니다.



일반적으로 Column Name은 NoSQL에서는 계속 반복되어 저장되므로 데이터의 용량이 커질 수 있습니다. 그래서 가급적이면 컬럼명은 알기 쉬운 범위에서 짤벡 정의하기를 권합니다. 예를 들어 first name이면 f_name으로 column 명을 정의하고 last name이면 l_name으로 정의해 사용합니다. 설계자와 개발자는 동일한 개념으로 인식해야 합니다.


아래는 emp_info라는 Column Family를 갖는 테이블 employee를 생성하는 예제입니다.


HBase(main):003:0> create 'employee', 'emp_info'
0 row(s) in 1.2200 seconds

HBase(main):003:0> list 'employee'

..

1 row(s) in 0.0550 seconds

HBase(main):004:0> put 'emploee', '1001', 'emp_info:l_name', '홍'

0 row(s) in 0.560 seconds

HBase(main):005:0> put 'emploee', '1001', 'emp_info:f_name', '길동'

0 row(s) in 0.0370 seconds

HBase(main):006:0> put 'employee', '1001', 'emp)info:gender', '남'


HBase(main):004:0> put 'emploee', '1002', 'emp_info:l_name', '강'

0 row(s) in 0.560 seconds

HBase(main):005:0> put 'emploee', '1002', 'emp_info:f_name', '감찬'

0 row(s) in 0.0370 seconds

HBase(main):006:0> put 'employee', '1002', 'emp)info:gender', '남'


저장된 row 중 필요한 row 하나를 조회하면 다음과 같습니다.


HBase(main):008:0>?get 'employee', '1001'


COLUMN????? CELL


emp_info:l_name, timestamp=1288380727188, value=홍

emp_info:f_name, timestamp=1288380738440, value=길동

emp_info:gender, timestamp=1288380747365, value=남


1 row(s) in 0.0400 seconds