어제 뉴스를 보면서 사람은 하루에 약 2Gb의 데이터를 자동차는 사람보다 더 많은 데이터를 비행기는 테라 바이트에 가까울 정도로 생산을 해낸다는 기사를 보았습니다.

저 또한 크롤러를 통해 하루에도 수많은 데이터를 만들어 내고 있습니다.

그렇다면 저 수많은 데이터를 관리를 해주는 곳이 필요할 텐데 그곳이 바로 database라는 시스템입니다.

하지만 데이터를 저장 하는 방식에 따라 크게 3가지의 종류로 나눠 볼 수 있습니다.

더 깊숙하게 들어가면 특정 어플리케이션에서만 사용하는 경량방식(SQLite)도 존재 하지만 이는 생략을 하도록 하겠습니다.

우선 빠르게 어떤 종류가 있는지 부터 훑어 보도록 하겠습니다.

이번 포스팅에서는 제가 여러 데이터 베이스를 사용하면서 각각의 데이터 베이스마다 어떤 식으로 데이터를 취급 하는지, 어떤 방식으로 데이터를 다뤄야 하는지 정리를 해보도록 하겠습니다.


1. 디비종류

1.1. RDB(Relational Database)

oracle, mysql등으로 가장 많이 알려진 RDBMS 즉 RDB 입니다.

1.2. NoSQL

대표적인 예로 mongodb, hbase가 있습니다
mongodb같은 경우는 mean이 널리 퍼지면서 많이 알려진 db중 하나입니다.
hbase같은 경우는 빅 데이터 처리를 한다면 누구나 한번쯤은 들어 봤을 겁니다.

1.3. In-Memory

대표적인 예로 redis가 있습니다

인 메모리 방식도 NoSQL방식에 속하지만 분리 해 보았습니다.

디비에서 가장 중요한 점은 각각의 데이터마다의 고유성을 만들어 내는 것입니다. 이것을 key 또는 Id라고 합니다. 아마도 대부분이 Key라는 용어를 사용 할 겁니다.

하지만 디비에 따라 약간씩 Key를 부르는 용어가 다른데 rdb에서는 PK(Primary Key), FK(Foreign Key), nosql에서는 rowKey, partitionKey, sortingKey, id등으로 불리우고 있습니다. 키에 따라 각 디비의 성격을 볼 수 있습니다.

데이터마다 고유성을 주기 위해 키를 생성한다고 언급을 하였습니다. 그런데 왜 디비마다 다른 키를 형성을 할 까요 또한 키를 만들지 않는 디비도 있습니다.


2. key, null

2.1. rdb - mysql, mariadb

rdb중에서 mysql, mariadb를 기준으로 설명을 시작하겠습니다.

해당 디비에서는 PK와 FK를 사용한다고 했습니다.

RDB는 관계형 데이터 베이스 입니다. 즉 둘의 관계를 나타내줄 수 있는 무엇인가가 필요한다. 그것이 키입니다.

PK는 해당 데이터의 고유성입니다. FK는 외래 키라고 하는데 관계에 대한 키입니다.

만약 친구라는 테이블이 있다고 가정을 해봅시다. 그럼 이 테이블에는 두명의 유저가 들어갈 것 입니다.

users table
| id  | user   |
----------------
| 0   | mung1  |
| 1   | mung2  |

friends table
| id  | user1  | user1  |
-------------------------
| 0   | 0      | 1      |

key

rdb라면 아마 위와 같은 구조로 데이터를 저장을 할 것입니다.

친구라는 테이블은 각각의 유저키를 저장을 하게 되는데 이떄 친구 테이블에서는 해당 키를 fk라고 부르게 됩니다.

저렇게 테이블을 나누어 사용하는 이유는 데이터의 중복을 막기 위함입니다.

만약 데이터가 유저 이름 말고 유저 이름, 나이, 전화번호가 있다고 가정을 해봅시다. 그럼 매번 유저 정보를 저장하기엔 저장 하는 측면에서 불 필요하게 저장을 하게 될 것입니다. 하지만 위처럼 정규화를 한다면 한 정보에 대해서는 한번의 저장만 이루어 지게 됩니다.

null

추가적으로 RDB에서는 근본적으로 null값을 허용하지 않기 때문입니다. 이유는 집합의 개념을 사용하기 때문입니다.

학창시절에 배운 집합을 생각해보면 집합에서 null이라는 개념을 공부한적이 없을것입니다. 여기서 공집합 == null이 아니냐고 의문을 가질 수 있지만 공집합의 경우 users테이블에서 id를 2를 검색을 하면 아무런 데이터가 출력이 되지 않습니다 이것을 공집합이라고 합니다.

갑자기 null을 왜 언급을 했을까요?

해당 디비를 사용하면서 null을 가장 많이 목격을 하는 부분이 join을 사용하는 경우 입니다. join은 집합을 통해 설명을 하자면 합집합, 교집합, 차집합, 여집합의 개념을 사용하는 방법입니다.

INNER JOIN, OUTER JOIN, LEFT JOIN, RIGHT JOIN이 있습니다.

즉 각각의 조인 방식은 키의 값을 어디로 기준을 둘건지가 되는데 inner join이 교집합의 개념을 사용하는 방식입니다. 즉 두개의 테이블을 조인을 할 때 해당 키가 양측에 다 있어야 데이터를 합치는 작업을 하게 됩니다.

inner join은 양쪽의 키값이 존재 해야 출력을 하는 방식입니다. 그렇기 때문에 빈 데이터가 있을 수 없습니다. 하지만 다른 조인 방식은 한쪽이라도 값이 성립하면 데이터를 만들어 내기 때문에 나머지 키에 대한 데이터를 null로 표시를 할 수 밖에 없습니다.

그렇다면 rdb는 어떨때 사용을 하여야 할까요?

rdb를 사용하기 위해서는 해당 데이터의 모델의 정의가 확실 해야 합니다.

데이터의 성격으로 부터 데이터를 정의를 하고 각 정의에 맞추어 테이블을 생성을 하고 중복 데이터는 쪼개면서 모델링을 하기 위해서는 데이터의 정의가 확실히 되어 있어야 합니다.

또한 rdb는 트랜젝션이라고 하는 각각의 처리단위를 굉장히 중요시 합니다.

rdb에서는 하나의 정보를 만들기 위해 여러 테이블로 쿼리를 사용하게 됩니다. 그렇기 때문에 모든 처리가 완벽히 끝남을 보장을 해주어야 합니다. (은행 업무를 보는데 이체를 하는데 이체중 에러가 떴다고 돈은 빠져나가고 받는 사람은 못받으면 화나겠죠? ㅎㅎ 에러가 발생 했다면 이체한 사람의 돈은 다시 복구가 되어야 합니다.)

즉, 데이터의 모델이 확실하고 하나하나의 처리가 확실해야 한다면 rdb를 사용을 고려해보아야 합니다.

2.2. NoSQL

NoSQL에서는 InMemory도 포함을 하여 설명을 해보도록 하겠습니다.

null

NoSQL을 수 개월동안 써왔지만 아직도 정확한 개념이 잡히지 않을 정도로 상당히 어려운 부분입니다.

같은 NoSQL일지라도 처로 다른 데이터 취급, 처리방식 null값의 허용유무 등이 너무도 제각각이라 상당히 난해한 부분이 있습니다.

수많은 사람들이 mongodb를 접하면서 처음에는 정말 쉬운 디비라고 하다가 시간이 지날수록 점점 어렵다라고 합니다. (저는 쿼리를 못 쓰는것 자체부터 쓰기가 싫었을 정도였습니다 아직도 싫어요 ㅋㅋㅋ)

nosql이 나온 배경을 살펴보면 이 세상의 모든 데이터를 모델링 하기에는 데이터가 넘쳐 납니다. 괜히 빅 데이터 시대가 아닙니다. 즉 데이터를 막 저장을 하자 다만 데이터의 고유성을 알아야 하니 키값만 만들어 두자

여기서 문제가 생기는 건 데이터를 막 저장을 하는 겁니다(여기서 막 저장이라는 말은 데이터를 정규화 하지 않고 저장을 하는 것을 의미합니다)

즉 데이터의 타입을 정하지 않습니다. 또한 미리 데이터를 정의를 하지 않게됩니다.

이 두가지는 처음 디비를 접하는데 있어서 접근성이 높을 수 밖에 없습니다. 하지만 시간이 지날수록 미리 정의 되지 않는 데이터의 모델 정의되지 않은 데이터의 타입으로 다양한 문제를 일으킬 수 있습니다.

이것을 해결 하기 위해서는 해당 디비를 사용하는 클라이언트, 즉 서버가 대부분 일 겁니다. 해당 서버에서 데이터를 validation하여 디비에 저장을 해야 위 문제를 어느정도 해결을 할 수 있습니다.

데이터를 일단 있는 그대로 저장을 하기 때문에 null값을 허용을 합니다. 하지만 허용을 하지 않는 디비도 존재는 합니다(대표적인 예가 dynamodb입니다)

널값을 허용하지 않는 디비는 사실 아직도 왜 null을 허용하지 않는지에 대해서 의문입니다.

key

위에서 키를 잠깐 언급을 했습니다.

nosql에서는 key를 상당히 다양한 용어로 부릅니다. id, rowkey, partitionkey, sortingkey, 마지막으로 엄밀히 따지면 키는 아니지만 컬럼을 그룹을 묶어주는 column family라는 개념도 등장을 합니다.

다른 용어로 부르는 만큼 제각각 역할이나 기능이 다릅니다.

2.2.1 mongodb

mongodb같은 경우는 데이터를 추가하게 되면 해당 데이터의 고유키인 id라는 값을 자동으로 생성을 해줍니다.

id값을 추가 하지 않아도 시스템 내부적으로 알아서 생성을 해주는 특징을 가집니다.

몽고디비 같은 경우는 NoSQL에서 가장 쉽게 사용할 수 있는 디비 중 하나입니다.

2.2.2 dynamodb

dynamodb는 참 지랄맛습니다.

우선 널값을 허용하지 않습니다 빈 스트링도 넣을 수가 없습니다. 이게 정말 스트레스 받는 부분입니다. 모든 인자에 대해 validate를 해주어야 합니다.

dynamodb같은 경우는 partitionKey와 sortingKey를 사용합니다.

우선 partitionKey는 데이터의 가장 첫번째 지정번호입니다. 다음 으로는 sortingKey가 있는데, 간단한 예시를 통해 각 키를 설명을 해보도록 하겠습니다

만약 학교라는 곳이 있으면 학교 partitionKey가 되고, 각각의 학년, 각각의 반을 sortingKey로 설정을 할 수 있습니다.

여기서 중요한 것은 partitionKey는 테이블당 하나씩 지정이 되지만 sortingKey는 복수개가 지정이 가능합니다.

여기서 우리는 특정 학교의 특정 학년 혹은 특정 반을 조회를 할 수 있게 됩니다.

1학년 전부 출력
학년에 상관없이 1반만 출력

이러한 형태의 데이터 조회가 가능합니다.

dynamodb에서는 데이터를 조회할 때 scanquery를 통해 조회가 가능합니다.

2.2.3 hbase

hbase또한 참 지랄 맛아요 이놈은 데이터를 바이너리를 하여 저장을 하기 때문에 저장 하는 것 부터가 난관입니다. 심지어 각 컬럼 키값또한 바이너리화 시키기 때문에 바이너리로 인코딩 해서 넣고 불러와서 디코딩해서 사용하고 이 과정을 해주어야 합니다.

물론 코드적으로 귀찮지만 이 것은 데이터의 타입을 통일 하겠다는 의지가 보이는 부분입니다.

hbase에서는 로우 키(rowkey)컬럼 패밀리(column family)를 사용을 하게 됩니다.

rowKey는 데이터의 고유성을 나타내는 키입니다. 데이터 하나가 추가가 되면 row가 생성이 되서 이를 rowKey라고 부르는 것 같습니다.

근데 hbase는 컬럼을 그룹단위로 묶습니다. n개씩 각각 그룹을 묶어서 컬럼 단위로도 출력이 됩니다. 여기서 컬럼을 그룹을 짓고 그 그룹 이름을 컬럼 패밀리라고 합니다


{
    cf: {
        key1: value,
        key2: value
    }
}

[cf][key1], [cf][key2]의 형태로 접근 하던 것들이

outkey를 컬럼 패밀리로 뽑으면서 [cf:key1], [cf:key2]의 형태로 사용이 가능 해졌습니다. : 전후로 컬럼 패밀리:컬럼 키가 되겠습니다.

또한 컬럼 패밀리 단위로 필터를 사용 할 수 있습니다.


3. 다루기 힘든 dynamodb님

디비마다 키를 다르게 해둔 이유는 그럴만한 이유가 있어서 입니다. 또한 아직 언급한 디비보다 언급하지 않은 디비가 더 많습니다. 아마 위의 방식이 아닌 다른 방식으로 데이터를 다루는 디비는 존재 할 것이고 또 그럴만한 이유가 있어서 겠지요. 디비는 상황에 맞추어서 해당 프로젝트의 성격에 잘 맞춰서 사용하면 될 것 같습니다.

추가적으로 dynamodb에 대해서 조금 설명을 해보겠습니다.

개인적으로 dynamodb가 가장 다루기 까다로운 것 같습니다.

위에서 scanquery를 사용하여 데이터 조회가 가능 하다고 했습니다. 왜 굳이 dynamo는 데이터 조회를 하는 방법을 2가지로 나누어 놨을까요?

당연히 똑같은 방식으로 조회를 하지 않습니다.

scan과 query의 가장 큰 차이점 중 하나는 key로 필터를 넣어야 하는지 안 넣어도 되는지입니다.

scan의 경우는 key로 필터를 넣지 않아도 됩니다. 반면에 query는 key로 반드시 필터를 넣어 주어야 합니다.

scan은 key를 필터를 넣지 않기 때문에 모든 데이터를 가져옵니다. 그리고 필터를 하여 출력을 하게 됩니다.

query같은 경우는 모든 필터가 적용 된 데이터를 받아오게 됩니다.

여기서 전자와 후자는 어떤 차이가 있을까요?

Dynamodb는 데이터를 한번의 요청으로 1Mb의 제한된 값이 있습니다. 즉 1Mb가 넘으면 재요청을 하여 데이터를 더 가져와야 합니다.

10mb의 데이터가 있다.
1~ 1,000,000

100,000번째 데이터가 1Mb시점이다.

scan의 경우는 필터를 어떻게 넣어도 1~100,000데이터만 뽑아서 필터를 하게 됩니다.

하지만 query는 1~1,000,000에서 필터를 하게 됩니다.

하지만 scan이든 query든 1Mb 이상의 값을 불러 오면 dynamodb에서 제공하는 SDK에서는 각 불러온 데이터 셋에다가 가장 마지막 key값 포함하여 반환을 해줍니다.

해당 키를 기준으로 재조회를 하면 1Mb이상의 데이터 조회가 가능합니다.

참고로 일단적인 텍스트 기준으로 1Mb정도면 10만줄 정도의 텍스트 양입니다.

데이터는 참 어려운 것 같습니다.


In-Memory

마지막으로 하드 기반이 아닌 메모리 기반의 디비를 살펴보도록 하겠습니다.

대표적으로 가장 많이 사용되는 디비는 REDIS(Remote Dictionary System)입니다.

말 그대로 데이터를 HDD가 아닌 memory에 저장을 하는 기술입니다.

get/put을 사용하여 데이터를 넣고 뺄 수 있습니다.

인 메모리 기반의 디비는 key:value의 형태로 저장이 되지만 redis의 경우 value가 단순한 objet가 아닌 자료구조를 갖는 점에서 다른 인 메모리 기반의 디비와 큰 차이를 보입니다.

string, set, Sorted Set, hasheds, list의 타입으로 저장을 할 수 습니다.

해당 타입은 자료구조에 나온 것들과 같은 개념입니다.

메모리는 휘발성인데 디비로 사용 할 수 있을까?

네 그래서 인 메모리도 하드를 사용을 하게 됩니다
초기 목적은 캐싱 서버로 사용하기 위해 나왔지만 요즘에는 하드 저장이 가능 해졌다고 합니다.
주로 세션 정보같은 잃어도 큰 손실이 없고 그냥 약간 화가 날만한 데이터를 저장을 하게 됩니다.

디스크에도 저장을 할 수 있다고 언급을 했습니다. 이것을 Persistence라고 합니다.

snapshotting, 과 AOF방식으로 디스크에 저장을 할 수 있습니다.

  • snapshotting : 순간적을 메모리에 있는 데이터를 디스크로 저장을 하는 방식 이때 block현상 발생

  • AOF : redis의 모든 write/update를 log파일에 기록, 기본적으로 non-block-call방식으로 처리

snapshotting은 block현상이 있지만 restart할 떄 속도가 빠르고 AOF는 block현상은 없지만 restart할 떄 속도가 느립니다.

이 두가지를 잘 혼용하여 사용하면 됩니다.