2022-06-27,   심건우

NoSQL

  • Not Only SQL -> 관계형 데이터베이스가 아닌 데이터베이스 (Not RDBMS)
  • 대표적인 특징
    1. 스키마가 따로 없기 때문에 데이터 구조를 유연하게 설정할 수 있음
    2. 독립적 데이터 모델 설계 덕분에 여러 서버에 분산시키기 편리함
    3. 데이터의 일관성, 정합성 등이 필요한 경우 부적합 (데이터 중복 발생 가능)
  • SQL, NoSQL 비교

image

이미지 출처

이번 포스팅에선, NoSQL 중 Document 기반 DB에 해당하는 MongoDB를 docker-compose를 통해 설치 해보고, python client로 간단한 CRUD 예제를 진행해보겠습니다.

MongoDB 상세 설명

Docker image Pull

  • MongoDB 이미지 pull (docker-hub 참고)
docker pull mongo

image

docker-compose ( MongoDB & Mongo-express )

  • docker-compose.yml 파일 작성 (docker-hub 참고, mongo-express는 시각화 도구 중 하나)
version: '3.1'
services:
  mongodb:
    image: mongo
    restart: always
    ports:
      - 27017:27017
    volumes:
      - ./mongodb:/data/db
    environment:
      - MONGO_INITDB_ROOT_USERNAME={아이디}
      - MONGO_INITDB_ROOT_PASSWORD={비밀번호}
      - MONGO_INITDB_DATABASE={시작DB 이름}
  mongo-express:
    image: mongo-express
    restart: always
    ports:
      - {사용할 포트}:8081
    environment:
      - ME_CONFIG_MONGODB_SERVER=mongodb
      - ME_CONFIG_MONGODB_PORT=27017
      - ME_CONFIG_MONGODB_ADMINUSERNAME={아이디}
      - ME_CONFIG_MONGODB_ADMINPASSWORD={비밀번호}
      - ME_CONFIG_BASICAUTH_USERNAME={아이디}
      - ME_CONFIG_BASICAUTH_PASSWORD={비밀번호}
    depends_on:
      - mongodb
  • 컨테이너 실행
docker-compose up -d
  • Mongo-express 접속 화면

image

python client CRUD 예제

  • 라이브러리 import
from pymongo import MongoClient
from pprint import pprint
  • client 연결
client = MongoClient(host='MongoDB host url', port=27017, username='아이디', password='비밀번호')
  • Database, Collection, Document 생성
    • MongoDB는 RDBMS처럼 empty Database를 생성할 수 없다. 할 필요가 없음 (schema-less)
    • Collection과 document가 생성될 때, Database 목록에서 해당 Database를 조회할 수 있음
# Database
epozen = client.epozen
# Collection
team_dt = epozen.team_dt
# Document ('_id' 지정 안 하면 알아서 고유 id 부여)
team_dt.insert_one({
#     '_id': 1,
    'name': 'member_1', # str
    'age': 26, # int
    'hobby': ['math'], # list
    'pet': {'dog': 'happy'}, # dict
    'score': 60.0 # float
})
  • Database 목록
client.list_database_names()

image

  • Collection 목록
epozen.list_collection_names()

image

  • Document 하나 삽입
team_dt.insert_one({
    'name': 'member_2',
    'age': 27,
    'hobby': ['soccer', 'music', 'math'],
    'pet': {'dog': 'happy', 'cat': 'kitty'},
    'score': 80.0
})

image

  • Document 여러개 삽입
team_dt.insert_many([
    {
        'name': 'member_3',
        'age': 28,
        'hobby': ['baseball', 'music'],
        'pet': {'cat': 'mike'},
        'score': 75.5
    },
    {
        'name': 'member_4',
        'age': 29,
        'hobby': [],
        'pet': {'dog': 'alice'},
        'score': 90.0
    },
    {
        'name': 'member_5',
        'age': 30,
        'hobby': ['music'],
        'pet': {},
        'score': 100.0
    },
])

image

  • Document 하나 조회, 조건 없이 수행 시, 맨 처음 삽입한 Document 가져옴
pprint(team_dt.find_one())

image

  • Document 전체 조회
for doc in team_dt.find():
    pprint(doc)

image

  • Document 조회, 조건 입력
for doc in team_dt.find({'age': 27}):
    pprint(doc.get('age'))

image

  • Document 조회, advanced query ($gt -> greater -> ~보다 큰)
for doc in team_dt.find({'score': {'$gt': 80}}):
    pprint(doc.get('score'))

image

  • Document 조회, 정규표현식 (이름이 3으로 끝나는), 문자열 데이터에만 적용 가능
for doc in team_dt.find({'name': {'$regex': '3$'}}):
    pprint(doc.get('name'))

image

  • Document 정렬 (오름차순)
for doc in team_dt.find().sort('score', 1):
    pprint(doc.get('score'))

image

  • Document 정렬 (내림차순)
for doc in team_dt.find().sort('score', -1):
    pprint(doc.get('score'))

image

  • Document 하나 수정
team_dt.update_one({'name': 'member_3'}, {'$set': {'name': 'test_3'}})
pprint(team_dt.find_one({'name': 'test_3'}))

image

  • Document 여러개 수정
team_dt.update_many({'name': {'$regex': '^member'}},
                   {'$set': {'name': 'test_name'}})
for doc in team_dt.find():
    pprint(doc)

image

  • 결과 반환 수 제한 limit()
for doc in team_dt.find().limit(3):
    pprint(doc)

image

  • Document 하나 삭제
team_dt.delete_one({'age': 30})
for doc in team_dt.find():
    pprint(doc)

image

  • Document 여러개 삭제
team_dt.delete_many({'score': {'$lt': 80}})
for doc in team_dt.find():
    pprint(doc)

image

  • Collection 내 모든 Document 삭제
team_dt.delete_many({})
for doc in team_dt.find():
    pprint(doc)
  • Collection 삭제
team_dt.drop()
pprint(epozen.list_collection_names())

image

정리

  • api를 기반으로 CRUD가 가능해서 보다 직관적으로 데이터를 다룰 수 있다.
  • 데이터가 정합성이나 일관성 등을 띄어야 하는 경우, 부적합 할 수 있다. (ex. 병원 환자 데이터, 은행 시스템 등)
  • 반면, 데이터 구조가 불분명하고, 데이터 변경 및 확장 발생 가능성이 있는 경우에는 적합할 수 있다. (ex. 공정 내 여러 종류의 센서 데이터, 대규모 로그 데이터 등)

추후에는 MongoDB의 최대 장점이라 할 수 있는 분산 처리 환경도 구성해 볼 예정이다.

업데이트: