본 포스트에서는 Flask와 Pymongo에 대하여 다루려고 한다.
mongoDB는 NOSQL을 다루는 대표적인 데이터베이스 중 하나이다.
NOSQL과 RDBMS
| NOSQL | RDBMS |
|---|---|
| 관계형 데이터 모델을 사용함, 데이터는 테이블로 구성 | 다양한 데이터 모델을 사용함, 주로 문서, 키-값, 그래프, 와이드 칼럼 스토어 등 |
| 정적인 스키마, 데이터베이스 생성 시 테이블 구조를 정의 | 동적인 스키마 또는 스키마리스를 가짐 |
| SQL(Structured Query Language)을 사용, 복잡한 쿼리와 다양한 조인을 지원 | 각 NoSQL 데이터베이스마다 별도의 쿼리 언어 또는 API를 사용 |
| 수평적 확장이 어렵고, 주로 수직적 확장을 통해 성능을 향상 | 주로 수평적 확장이 가능, 대부분의 NoSQL 데이터베이스는 분산 데이터베이스 아키텍처를 채택 |
| 복잡한 관계와 트랜잭션 처리가 필요한 경우에 주로 사용 | 대용량 및 실시간 데이터를 다루는 빠른 속도의 웹 애플리케이션, 로그 및 센서 데이터 처리 등에 적합, 유연성이 필요한 비정형 또는 반정형 데이터에 적합 |
이러한 특성들 때문에, 본 프로젝트에서는 NOSQL을 채택하였다. 프로젝트 기획단계에서는 실시간으로 소켓을 통하여 클라이언트에서 보내는 프레임데이터가 0.1초에 1개 예를들어 유튜브 영상이 5분이라는 길이를 가진다면 3000개의 데이터를 보내는 것이다. 이 데이터를 유용하게 저장하고 처리하기 위해서는 NOSQL형태의 데이터베이스를 선택하는 것이 유리하다고 생각하였다.
그러나 특히 이번 프로젝트에서 NOSQL의 큰 주의사항에 대해서 알게 되었다. 몽고DB의 경우 프라이머리키를 따로 지정해주는 방식이 아니라 고유의 ID를 가진다. 따라서 프라이머리키의 필요성이 없는 것이다. 각 테이블에 저장된
이러한 형태와 같이어떠한 형태를 가지고 있든 전혀 상관이 없다. 일련의 문자열이 생성되고 이것을 프라이머리키처럼 가져서 구분한다. 따라서 나머지 정보가 모두 같은 데이터베이스가 여러개 생길 수도 있는 부분이다.
결국 NOSQL도 우리 프로젝트 구조상 RDBMS처럼 다른 테이블과의 관계들을 가지고 있기 때문에, 이러한 부분을 주의해야하는 부분이 컸다. 당연히 코드는 데이터베이스의 무결성등을 지키도록 설계되었으나, 실제로는 코드 구조상 프라이머리키처럼 사용하는 인덱스라는 필드가 고유한 값을 가져야하는데, 병렬적인 작업을 수행하는 과정에서 일부 인덱스들이 중복되는 케이스(병렬적인 작업을 처리하는 과정에서 다음 인덱스를 계산하는 과정이 중복되는 케이스가 발생)들도 발생하였다.
예를 들면, _id : odjectId('650eb38679372e04f70b57f8')
이러한 형태와 같이 일련의 문자열이 생성되고 이것을 프라이머리키처럼 가져서 구분한다. 따라서 나머지 정보가 모두 같은 데이터베이스가 여러개 생길 수도 있는 부분이다.
결국 NOSQL도 우리 프로젝트 구조상 RDBMS처럼 다른 테이블과의 관계들을 가지고 있기 때문에, 이러한 부분을 주의해야하는 부분이 컸다. 당연히 코드는 데이터베이스의 무결성등을 지키도록 설계되었으나, 실제로는 코드 구조상 프라이머리키처럼 사용하는 인덱스라는 필드가 고유한 값을 가져야하는데, 병렬적인 작업을 수행하는 과정에서 일부 인덱스들이 중복되는 케이스(병렬적인 작업을 처리하는 과정에서 다음 인덱스를 계산하는 과정이 중복되는 케이스가 발생)들도 발생하였다.
본 프로젝트에서도 이러한 현상이 발생하였는데, 다른 주요한 기능들을 처리할 때 잘못된 데이터베이스들을 검사하여 제대로된 데이터베이스로 새로 구성하도록 처리하는 과정으로 해결하였다.
이제 플라스크를 이용하여 몽고디비를 이용하는 기본적인 내용들에 대해서 말해보려고 한다.
먼저 파이썬 가상환경에서 pip install pymongo를 통하여 인터프리터가 파이몽고 모듈을 처리할 수 있도록한다.
다음 코드를 통하여 파이몽고 모듈을 사용할 수 있다.
from pymongo import MongoClient
그리고 몽고디비를 연결한다. 이때, 다른 클라우드같은 곳에서 몽고디비를 불러오는 것이 아니라 컴퓨터에 설치하여 내장된 로컬 몽고디비 데이터베이스를 이용한다면 'mongodb://localhost:27017/'이다.
# MongoDB 연결 설정
client = MongoClient('mongodb://localhost:27017/')
db = client.데이터베이스이름
변수로 사용할 콜렉션 이름 = db.실제콜렉션이름
find_one은 가장 앞의 도큐먼트 하나만 검색하며 find는 모든 도큐먼트를 검색한다.
또한 가장 마지막줄은 이렇게 찾은 도큐먼트의 필드이름2가 가진 value값을 가져오는 것이다.
이 때 주의해야하는 점은, 이 find나 find_one 메소드들은 파이썬에서 지원하는 메소드가 아니라 pymongo에서 지원해주는 메소드이다.
이것은 확실한 내용은 아니고 추측이지만 따라서 그에 따라, find나 find_one은 매번 DB에서 검색하는 메소드일 수 있다. 시간 측정을 통해서 확인해봐야하는 부분일 수는 있지만, 그에 따라 대량의 작업을 진행할 때 find를 매번 해주는 것보다는 컴퓨터의 메모리에 미리 데이터베이스의 필요한 내용들을 가져온 후에 파이썬의 기본적인 딕셔너리나 반복문으로 처리해주는 것이 훨씬 빠를 것으로 예상된다.
filter_query = {'필드이름1': 1,} #필드이름1의 벨류가 1
document = 변수로 사용할 콜렉션 이름.find_one(filter_query)
my_new_data = document['필드이름2']
update를 위해서는 다음과 같이 메소드를 사용한다. 이때, 값을 증가시킬때는 inc, 값을 변경할 때는 set을 이용한다. 또한 가장 앞의 한개의 항목만 update한다면 update_one이고 모든 항목을 update한다면 update_many를 사용한다.
filter_query = {'필드이름1' : 1}
update_query = {'$inc': {'필드이름2': +1}}
update_query2 = {'$set': {'필드이름2': 1}}
result = 변수로 사용할 콜렉션 이름.update_one(filter_query, update_query)
insert시에는 다음과 같이 메소드를 사용한다.
변수로 사용할 콜렉션 이름.insert_one({
'필드이름1' : 1
})
delete시에는 다음과 같이 메소드를 사용한다. 만약에 여러개를 삭제하고 싶다면, delete_many를 사용하면 된다.
filter_query = {'필드이름1': 1,} #필드이름1의 벨류가 1
result = 변수로 사용할 콜렉션 이름.delete_one(filter_query)