1.데이터 베이스 분류
1) RDBMS : 테이블 기반, SQL을 이용해서 작업하는 관계형 데이터베이스(트리구조)
삽입 갱신 삭제(C U D) 작업에는 관계형 데이터베이스가 좋다
2) NoSQL (Not Only SQL) :
RDBMS 시스템의 주요 특성을 보장하는 ACID(Atomicity, Consistency, Isolation, Durability - Transaction의 주요 성질) 특성을 제공하지 않는 확장성이나 성능의 특성을 갖는 비관계형 데이터베이스
읽기(R) 작업에는 비관계형 데이터베이스가 좋다. 해싱을 통해 조회속도가 빠르다
틀이 없기 때문에 모든 데이터가 다 들어간다 But, 유효성 검사가 어렵다
주로 빅데이터, 분산 시스템 환경에서 대용량의 데이터를 처리하는데 적합함.
- 최근에는 NoSQL에 관계형 데이터베이스의 트랙잭션의 개념을 도입시키고 관계형 데이터베이스에도 NoSQL의 개념을 도입하는 형태로 발전하고 있다.
2. No SQL 종류
1) Key Value DB
- Key 와 Value 형태로 저장하는 데이터베이스 (dict, table, map)
Redis가 대표적인 Key Value인 데이터베이스
2) Document DB
- 하나의 데이터를 하나의 문서로 취급, MongoDB가 대표적인 Document DB
3) Wide Column Store
- 열의 집합체를 만들 수 있는 데이터베이스
HBase 나 Cassandra 가 대표적인 Wide Column Store
4) Graph DB
- 데이터에 그래프 자료구조를 도입한 데이터베이스
SNS에 주로 이용 ( 링크 리스트 구조 [정해진 방향이 없다])
3. MongoDB
1) 개요
- 데이터를 하나의 문서로 취급하는데 데이터를 표현할 때 Json 표기법(자바스크립트 객체 표현법, 파이썬도 동일) 사용
** Json표기법 - ex) 배열 : [데이터 나열] / 객체(dict) : { 키 : 값, 키 : 값 ...}
Mongo DB는 자바스크립트 문법으로 데이터를 다룬다.
MEAN [Mongo DB / Express.js / Angular.js / Node.js] : 자바스크립트 만으로 Wep application을 제작하는 기술, Angular.js 서비스가 종료 됨에 따라 현재는 MER(React.js)N
2) docker를 이용한 MongoDB 설치
- mongo 이미지를 다운로드 받아 컨테이너 이름으로 컨테이너 생성 후 data/ dat/db 라는 디렉토리에 데이터를 저장, 백그라운드로 실행하고 27017 번 포트를 27017 포트로 접속할 수 있도록 실행
docker run --name 컨테이너 이름 -v ~/data/data/db -d -p 27017:27017 mongo
docker run --name JHMongoDB -v ~/data/data/db -d -p 27017:27017 mongo
3) bash shell 에 접속
** shell : 운영체제와 사용자 사이의 인터페이스
** bash shell : shell 종류 중에 가장 많이 사용되는 shell
- docker 컨테이너 안에 접속
docker exec -it 컨테이너 이름 bash
docker exec -it JHMongoDB bash
4) Mongo DB Manual
- https://docs.mongodb.com/manual/crud/
Mongo DB Document
5) Mongo DB 작업
- 로컬에서 직접 설치한 경우 터미널에서 접속해 사용할 수 있고 도커에 설치한 경우 BASH SHELL에 접속해서 명령어를 실행할 수 있다.
Compass 나 Robo 3T와 같은 GUI Tool을 이용해서 작업을 수행할 수 있다.
6) 구성요소
- 데이터베이스 : 가장 큰 단위
- 컬렉션 : 테이블의 개념, 미리 생성하거나 구조를 만들 필요가 없다.
- 도큐먼트 : 하나의 데이터로 관계형 데이터베이스의 행의 개념
- 필드 : 관계형 데이터베이스에서 열의 개념
- 인덱스 : 데이터에 빠르게 접근하기 위한 객체
- Join이 없는 대신 Embedding 이나 Linking의 개념을 가짐.
(데이터 안의 필드에 다른 테이블을 포함 시킬수 있다.)
- select 문의 결과로 관계형 데이터 베이스는 Row의 집합을 반환 하지만 Cursor를 반환
** RDBMS 와 Mongo DB 용어 비교
RDBMS -> Mongo DB
Database -> Database
Table -> Collection
Row -> Document
Column -> Field
Index -> Index
Join -> Embedding & Linking
Select 구문의 결과로 Row의 집합을 반환 -> Cursor 반환
7) CRUD
(Create - 데이터 삽입, Read - 데이터 조회, Update - 데이터 갱신, Delete - 데이터 삭제)
> 데이터 입력
db.컬렉션이름.insertOne({ 키:값, 키:값 ...})
db.컬렉션이름.insertMany([{ 키:값, 키:값 ...},{ 키:값, 키:값 ...}..])
db.컬렉션이름.insertOne({})
db.mycollection.insertOne({"name" : "Kim J"})
db.mycollection.insertOne({"height" : 170, "weight" : 66})
키는 문자열이고 값은 null, 숫자, 문자, 객체, 배열, boolean, 날짜 등이 가능
데이터를 삽입할 때 Objectld 라는 타입의 _id 라는 키 값이 같이 삽입된다.
ex)
db.users.insertOne({"name":"park", "age":34}) - 1
db.users.insertOne({"nik":"kim", "height":170}) - 2
db.users.insertOne({"name":"park", "age":"34"}) - 3
1,2 / 1,3 / 1,2,3
모두 입력이 되긴 하지만 합당하지 않다. 비교하기가 어렵다. 따라서 입력 시 주의해야 함
> 데이터 확인
db.컬렉션이름.find({});
db.mycollection.find({});
> 현재 데이터베이스 확인
db;
> 전체 데이터베이스 확인 (데이터가 존재하는 것 만 확인가능)
show dbs;
> 데이터베이스 생성 및 사용
use 데이터베이스이름;
use jhmongo;
switched to db jhmongo / 만들고 쓰는게 아니라 그냥 쓰면 만들어진다.
> 데이터베이스 삭제
db.dropDatabase();
> Capped Collection
정해진 크기를 초과하면 자동으로 가장 오래된 데이터를 삭제하는 Collection (관계형 데이터베이스와 다른점 중 하나)
디스크가 한정 된 상황에서 로그 데이터나 분석 결과를 저장해야 하는 경우 사용함
-- Capped Collection 생성
db.createCollection('cappedCollection',{capped:true, size:10000});
-- 데이터 삽입
db.cappedCollection.insertOne({x:1});
-- 데이터 확인
db.cappedCollection.find({});
-- 데이터 여러개 삽입 ( 반복문 가능 )
for(i=0, i<1000; i++){db.cappedCollection.insertOne({x:1})};
> View
[★★관계형 데이터베이스의 View와 다름]
데이터베이스 안에 쓸수는 없고 읽을 수만 있는 데이터베이스 개체
View 를 만들게 되면 읽기 속도가 빨라지고 보안을 유지할 수 있다.
(메모리에 저장해 두고 불러오기 때문에 파일에서 불러오는 것 보다 빠름)
8) Thread 스레드
- Mongo DB는 기본적으로 싱글 스레드 / 멀티 스레드로 작업 가능
- Program, Process, Thread
Program : 동일한 목적을 달성하기 위한 파일의 집합
Process : 실행 중인 프로그램 ( 프로그램을 실행하면 프로세스 )
Thread : 프로세스 안에서 자원을 할당해 수행하는 작업 단위
실행 중간에 다른 스레드로 제어권을 넘길 수 있다.
** Process와 다른점 : 단독으로 실행 될 수 없고 반드시 프로세스 안에서 실행되어야 함
프로세스 안에는 반드시 1개 이상의 스레드가 존재한다.
** 함수를 그냥 실행하면 함수는 수행이 종료 될 때까지 다른 함수를 수행하지 못하지만 함수를 스레드로 실행하면 수행 중간에 다른 스레드를 실행하고 돌아올 수 있다.
(쉽게 말해서 집에서 밥을 밥솥에 취사해두고 청소를 하고 빨래를 하러 세탁기에 다녀올 수 있다. cpu는 1개지만 여러작업 수행. but 이것저것 불필요한 부가적인 작업이 많으면 왔다갔다 하느라 시간을 다보낸다. )
> python에서 스레드 프로그래밍
#스레드를 만들지 않고 2개의 함수를 호출 > 20초 정도
import time
def threadex(id):
for i in range(10):
print("id={0} --> {1}".format(id,i))
time.sleep(1) # 1초 쉬었다 반복 한다
for i in range(2):
threadex("{0}번 스레드".format(i))
# 2개의 스레드를 만들어서 함수를 호출 > 10초 정도
import time, threading
def threadex(id):
for i in range(10):
print("id={0} --> {1}".format(id,i))
time.sleep(1) # 1초 쉬었다 반복 한다
for i in range(2):
id = ("{0}번 스레드".format(i))
th = threading.Thread(target=threadex, args=(id, )) # 스레드를 만든 것 args 튜플로 줘야함
th.start() # 스레드 실행
> Asynchronous(비동기)
동기 : 순서대로 하나씩 실행
비동기 : 수행 중에도 다른 작업으로 제어권을 넘길 수 있는 것
스레드 프로그래밍과 유사하게 사용
인터넷이 느린곳에서는 비동기를 사용할 경우 오래걸림
(한국에서 쓰는 네이버 메인 포탈에서는 많이 쓰지만, 전 세계에서 많이 쓰는 구글 메인에는 없다.)
> 스레드 프로그래밍에서 주의할 점
공유 자원 수정 문제 발생 시
Lock 활용하여 세마포어 형식 적용
import time, threading
# 전역 변수로 사용할 변수
g_count = 0
# 공유자원을 제어하기 위한 변수
lock = threading.Lock()
# 상속을 이용한 스레드 클래스 생성
class threadex(threading.Thread):
# 스레드로 수행 할 메서드
def run(self):
# 외부에서 만든 g_count, lock 사용
global g_count
global lock
for i in range(10):
lock.acquire()
print("id={0} 증가하기 전 --> {1}".format(self.getName(), g_count))
g_count = g_count + 1
time.sleep(1)
print("id={0} 증가한 후 --> {1}".format(self.getName(), g_count))
lock.release()
time.sleep(1)
for i in range(2):
th = threadex()
th.start()
생산자와 소비자 문제
Dead Lock
9) Mongo DB에서의 멀티 스레드를 이용한 데이터 삽입
> name을 인덱스로 설정해 유일무이하게 저장하도록 컬렉션을 생성
db.컬렉션이름.createIndex({name:1},{unique:true});
db.sample.createIndex({name:1},{unique:true});
> 데이터 삽입
db.sample.insertOne({name:"adam"});

> 여러 개의 데이터 삽입
db.sample.insert({name : "itstudy"}, {name : "adam"}, {name : "ggangpae1"});
adma이 중복이기 때문에 오류 발생 후 rollback 된다.
> 데이터 확인
db.sample.find({});
데이터 삽입 중 오류가 발생 했지만, NoSQL은 싱글 스레드 형태로 동작하기 때문에 itstudy는 삽입이 되고 ggangpae1은 삽입이 되지 않음

> 멀티 스레드를 이용해 삽입을 원할경우, {ordered : false} 라는 옵션 추가
> 멀티 스레드를 이용해 여러개의 데이터 삽입
db.sample.insert([{name : "itstudy"}, {name : "adam"}, {name : "ggangpae1"}], {ordered:false});
> 데이터 확인
db.sample.find({});
모든 데이터가 각각의 스레드로 동작하기 때문에 하나의 스레드에 오류가 발생 하더라도 나머지 스레드는 정삭적으로 수행한다. itstudy와 adam을 삽입하려고 하는 것은 오류가 발생하지만 ggangpae1은 별개로 삽입된다.

** CQRS 패턴
CRUD(Create, Read, Update, Delete)에서CUD(Command)와 R(Query)을 구분하자는 이야기다.구분하는 이유는,우리가 Database로부터 데이터를 읽어오고 처리를 하게 되면이미 그 사이에 데이터가 변경이 되었을 가능성이 높다.CQRS는 이런 변경 가능성을 인정하고 어차피 Read와 CUD 사이에는 delay가 존재할 수 있음을 인정하는 것이다.이를 통해서 R과 CUD를 구분함으로써 얻는 이점을 설명하는 것이 CQRS패턴이다.
데이터 작업을 위한 Application ↔ RDBMS
↓↑
Message Broker = Kafka
↓↑
데이터 읽기을 위한 Application ↔ NoSQL
CQRS 패턴을 통해 얻을 수 있는 이점은 여러 가지가 있다.
Read와 CUD 각각에 더 최적화된 Database 구성을 통해서 성능을 더 향상시킬 수 있다.
Read와 CUD에서 필요한 데이터 형식이 다를 수 있고, 특히 Read는 aggregation(집계 함수) 등의 부가적인 attribute들이 Entity에 필요하게 될 수 있다. R과 CUD를 분리함으로써 R로 인해 Entity의 구조가 변경되는 것을 막을 수 있다.
R과 CUD를 분리함으로써 과도하게 복잡한 모델을 덜 복합하게 만듦으로서 시스템 복잡도를 줄일 수 있다.
'DX Data School' 카테고리의 다른 글
Python 과 Mongo DB 연동 (1) | 2024.01.30 |
---|---|
Python의 Exception Handling(예외처리) (1) | 2024.01.30 |
Windows Function (0) | 2024.01.30 |
Transaction 실습 (0) | 2024.01.30 |
[SQL]DDL, DML, DCL, DQL, TCL 개념과 종류 (0) | 2024.01.30 |