Gom3rye

Mongo DB 본문

현대 오토에버 클라우드 스쿨

Mongo DB

Gom3rye 2025. 5. 20. 10:33

Mongo DB

  • NoSQL
  • 읽기에 특화: JOIN이 없음
    • Linking: 참조를 기억시킴(몽고는 프로그래밍 언어랑 비슷하니까)
      • 단점: 뭐 하나를 지우려고 하면 그 참조도 지워야 한다. ⇒ 삽입과 삭제에는 약하고 삽입과 삭제가 빈번하면 느려진다.
    • Embedding: 다른 문서의 데이터를 중첩 구조로 하위 문서로 포함시킴
      • 단점: 하나의 레코드의 길이가 너무 길어져서 보기가 어렵고 사이즈가 크면 속도가 느려진다.
    CQRS: 읽기와 쓰기를 분리해라!! (읽기: 쓰레드 이용해서 동시에 작업, 쓰기: 를 이용해서 1개씩 작업 → 작업의 구조자체가 다르다.
    • 읽기와 쓰기의 비율이 동일할 가능성이 별로 없다.)
    ex. "읽기는 MongoDB, 쓰기는 MySQL, 그리고 Message Queue로 연결”
    • 사용자 요청은 MySQL(RDBMS)에 기록 (쓰기)
    • 변경된 데이터를 Message Queue (Kafka, RabbitMQ 등)에 Publish
    • MongoDB가 그 메시지를 Subscribe해서 읽기용 데이터로 저장 (Read Model 구축)
    • 프론트는 MongoDB에서 빠르게 조회
    이는 CQRS 패턴 + 비동기 처리를 구현한 구조이다.
  • 🔸 읽기/쓰기 분리 설계한 사용 예시:

fk를 설정하지 않아도 합칠 수 있지만 없으면 full table 스캔을 해야 한다. (fk는 pk이거나 unique 해야 하니까 하나만 존재함 → 한 번 찾으면 더 이상 찾을 필요가 없다. (full table scan 하지 않아도 된다.))

  • MongoDB, MySQL - 한 db에 여러 사용자 존재 (db가 유저보다 큰 개념 → 항상 use {db이름}으로 시작)
  • Oracle - 한 유저에 db 존재 (유저가 db보다 큰 개념)

데이터베이스 상태 조회

db.getCollectionInfos() # 현재 데이터베이스의 Collection들의 정보 조회

Collection의 상태 조회

  • Collection 목록 조회
show collections # cappedCollection
  • Collection 정보 리턴
db.컬렉션이름.stats() # db.cappedCollection.stats()
  • 지연 시간 확인
db.컬렉션이름.latencyStats() # db.cappedCollection.latencyStats()
  • Collection 크기 확인
db.컬렉션이름.storageSize() # db.cappedCollection.storageSize()  result: 24576
  • 인덱스 크기 확인
db.컬렉션이름.totalIndexSize() # db.cappedCollection.totalIndexSize()  result: 20480
  • 전체 크기 확인 (collection 크기 + 인덱스 크기)
db.컬렉션이름.totalSize() # db.cappedCollection.totalSize()  result: 45056

Collection의 전체 데이터 조회

db.컬렉션이름.**find()** # db.cappedCollection.find()

Document 생성 (데이터 삽입)

특징

  • 단일 Collection을 대상으로 수행한다.
  • 단일 Document 레벨에서 원자적으로 수행된다. (하나씩 된다.)
  • JSON 표현식으로 데이터를 만들어서 삽입한다.
  • Collection을 만들고 삽입해도 되고 Collection이 없는 경우 처음 삽입하면 Collection이 만들어진다.
  • Document를 만들 때 _id라는 key를 만들지 않고 생성하면 자동으로 _id라는 key가 생성되면서 ObjectId를 부여해서 데이터를 구분한다.
  • 삽입하는 함수: insert(deprecated 되었다.), save, insertOne, insertMany
    • insert는 동일한 _id를 갖는 데이터를 추가하면 ERROR, but save는 데이터를 수정한다.

Insert()

일반 객체 삽입

db.users.insert({name:"adam", age:25, gender:'man'}) # users 컬렌션 만듬

객체 안에 객체와 배열이 존재하는 경우

db.inventory.insert({item:"ABC1", details:{model: "14Q3", manufacture: "xyz Company"},
                     stock: [{size: "s", qty: 25}, {size: "m", qty: 50}], 
                     category: "clothing"})
  • → users, inventory 컬렉션들 없었는데 show collections 해보면 생긴 것을 확인할 수 있다. → acknowledged: true 나오면 명령 성공한 것

여러 개의 데이터를 한꺼번에 삽입한 경우

  • [ ]를 이용해서 배열 안의 데이터들을 쪼개서 넣음
db.users.insert([ {name: "군계"}, {name: "쥬니"} ])
# 또는
db.users.insertMany([
  { name: "군계", age: 25 },
  { name: "쥬니", age: 30 },
  { name: "Charlie", age: 22 }
])
  • insert 함수의 두 번째 매개변수는 생략 가능한데 ordered 옵션을 설정해서 첫 번째 매개변수의 데이터가 배열일 때 싱글 스레드를 이용할지 멀티 스레드를 이용할지를 설정할 수 있다. 이때 true 옵션을 설정하면 싱글 스레드, false를 설정하면 멀티 스레드
    • 싱글 스레드 사용: 중간에 에러가 발생하면 뒤의 데이터는 삽입되지 않는다.
    • 멀티 스레드 사용: 중간에 에러가 발생하더라도 뒤의 데이터 삽입된다.

ex. sample이라는 컬렉션을 생성하는데 name을 index로 설정 (인덱스를 만들고 unique 속성 설정 가능)

db.sample.createIndex({name: 1}, {unique: true})

  • sample 데이터 생성
db.sample.insert({name: "군계"})
  • 싱글 스레드 사용해서 여러 개의 데이터 삽입
db.sample.insert([{name: "쥬니"}, {name: "군계"}, {name: "헨리"}], {ordered: true})
# {ordered: true}: 명시적으로 싱글 스레드 사용하고 싶으면 사용
  • 쥬니는 정상적으로 삽입되지만 군계는 중복된 키 사용으로 에러가 발생해서 군계 이후 데이터는 삽입되지 않는다. (스레드 중단)
  • 멀티 스레드 사용해서 여러 개의 데이터 삽입
db.sample.insert([{name: "쥬니"}, {name: "군계"}, {name: "헨리"}], {ordered: false})
  • db.sample.find() 로 데이터를 조회하면 쥬니와 군계가 중복돼서 키 에러가 발생하지만 헨리 데이터는 다른 스레드로 삽입하기 때문에 삽입 성공

⇒ MongoDB에서 삽입할 때 옵션을 설정하는데 없는 옵션을 설정해도 에러가 발생하지 않는다.

이유는 모든 옵션을 읽어서 적용하는 게 아니라 필요한 옵션만 추출해서 적용하기 때문

db.sample.insert({name: "비앙카"}, {noption: true})

ObjectId

  • RDBMS 에서는 Sequence나 auto_increment 같은 옵션이나 객체를 이용해서 일련번호를 생성해서 사용
  • MongoDB 에서는 12 byte로 구성된 ObjectId를 제공
  • 컬렉션을 만들면 _id라는 속성으로 ObjectId를 생성해서 할당하는 방식으로 일련번호를 부여
  • new ObjectId()를 이용해서 직접 생성 가능

ex. 직접 생성해서 만드는 방법

let newId = new ObjectId()
db.sample.insert({_id: newId, name: "user01"})
# 확인은 db.sample.find()로!

insertOne()

  • 하나의 데이터를 삽입하고자 할 때 사용하는 함수
  • 데이터를 삽입하면 성공 여부와 ObjectId를 리턴
db.user.insertOne({username: "karoid", password: 1111})

writeConcern

  • 삽입, 삭제, 갱신하는 명령은 writeConcern 옵션을 제공하는데 이 옵션은 데이터 손실을 방지하기 위한 옵션이다.
  • 일반적으로 삽입, 삭제, 갱신 명령은 저장 장치에 바로 명령을 수행하는 것이 아니라 메모리에 작업을 수행한 후 나중에 저장 장치에 반영을 하는데 이런 경우 데이터베이스에 갑자기 문제가 생기면 메모리에만 저장된 데이터를 잃어버릴 수 있기 때문에 이를 방지하기 위한 옵션 → 디스크가 여러 개일 때 유용한 명령
  • 구독해서 여러 디스크들끼리 싱크 맞출 수 있게 해준다.

insertMany()

  • 여러 개의 데이터를 삽입하고자 하는 경우 사용
db.컬렉션이름.insertMany([데이터 나열], {writeConvern: { }, ordered: <boolean> })

db.user.insertMany([
		{username: "John", password: 4321},
		{username: "Kyla", password: 4221},
		{username: "Mark", password: 5321}
])

데이터 조회

단일 Collection에서만 Document를 선택할 수 있다. (JOIN 없으니까)

Cursor: 결과를 가지고 하나씩 접근할 수 있도록 해주는 객체

find()

find(query, projection)

  • selection: 데이터를 조회하기 위한 조건 (MySQL에서 where절)
  • projection: 컬럼 단위 추출 (MySQL에서 select절)
  • Cursor가 리턴된다.
  • 컬렉션의 전체 데이터 조회
db.컬렉션이름.find()
db.users.find()
db.containerBox.find({},{_id:false}) # id 안 보이고 실제 데이터만 보이도록

(MongoDB Compass를 이용하면 더 쉽게 collection을 만들어서 json 파일들을 넣을 수 있다.)

  • query
    • 원하는 조건을 작성하는 옵션
    • ex. containerBox 라는 컬렉션에서 name이 가위이고 size가 30인 데이터 조회
    • db.containerBox.find({name: "가위", size: 30})

샘플 데이터 삽입

db.containerBox.insertMany([
	{name: "bear", weight: 60, category: "animal"},
	{name: "bear", weight: 10, category: "animal"},
	{name: "cat", weight: 3, category: "animal"},
	{name: "phone", weight: 1, category: "electronic"}
])

category가 animal이고 name이 bear 인 데이터 조회

db.containerBox.find({name: "bear", category: "animal"})

. 연산자

객체 안의 속성을 불러올 때 사용한다.

let myvar = {hello: "world"}
myvar.hello # world
  • 객체 안에 존재하는 객체의 속성을 사용하고자 하는 경우 {name: {firstName: "karoid", lastName: "Jeong"}} ex. name 안에 있는 firstName의 값이 karoid 인 데이터를 조회
  • db.컬렉션이름.find({ "name.firstName": "karoid" })
  • 배열 안의 데이터 조회
    db.컬렉션이름.find({ "numbers.0": 52 })
    
  • {numbers: [101, 32, 21, 11]} {numbers: [64, 32, 21, 11]} {numbers: [52, 32, 21, 11]} 배열 안의 첫 번째 요소 값이 52인 데이터 조회 ⇒ .0
  • projection
    • 원하는 컬럼을 추출하는 것
    • find 두 번째 옵션으로 설정
    • true나 1로 설정하면 출력되고 false나 0으로 설정하면 조회되지 않는다.
    • {컬럼이름: <Boolean>, 컬럼이름: <Boolean> ...}
    • containerBox 컬렉션에서 name을 조회
    • db.containerBox.find({}, {name: true}) # {}는 query 부분 → "조건 없이 전체 문서를 가져와라" 라는 의미 # if _id 안 나오고 name만 나오도록 찾고 싶다면 db.containerBox.find({}, {_id: 0, name: true})

Cursor

질의에 대한 포인터

  • Mongo DB는 find의 결과로 커서를 리턴한다.
  • find의 결과를 저장하고 그 저장한 커서를 이용해서 next 함수를 호출하면 다음 데이터가 호출되고 hasNext 함수를 호출하면 다음 데이터의 존재 여부를 리턴한다.
    • cursor.next() 로 불러오면 메모리를 절약할 수 있다. (하나씩 불러오니까)
db.cappedCollection.find() # 쫘르륵 보이고
let cursor = db.cappedCollection.find() # 아무 일도 안 일어난 것처럼 보인다.

Document 수정

replaceOne()

db.컬렉션이름.replaceOne(
	<query>,
	<replacement>,
	{
		upsert: <boolean>,
		collation: <document>
	}
)
  • query: 수정할 데이터를 추출
  • replacement: 수정할 내용
  • upsert: 조건에 맞는 데이터가 없을 때 추가 여부
  • collation: 악센트나 대소문자 관계에 대한 순서를 설정
db.user.replaceOne(
	{username: "karoid"}, 
  {username: "Karoid", status: "Sleep", points: 100, password: 2222})

db.user.find({username:"Karoid"}) # 로 확인해보기

  • upsert 옵션
db.myCollection.find() # 없는 거 확인함
db.myCollection.replaceOne(
	{item: "abc123"},
	{item: "abc123", status: "P", points: 100},
	{upsert: true}) # {upsert: true}가 없으면 매치된 데이터가 없기 때문에 find를 해도 아무런 데이터가 나오지 않음
	# upsert를 하면 없는 경우 새로 만들어준다.

updateOne()

조건에 맞는 데이터가 여러 개더라도 1개만 수정

→ 이 함수들은 $set 연산자를 이용해서 수정할 내용을 작성해야 한다.

ex. 아무 조건 없이 첫 번째 문서를 선택해서 username 바꾸기

db.user.updateOne({}, { $set: { username: "Modified" } })

구문 의미

replaceOne({}, { ... }) ✅ 전체 문서를 새 객체로 교체
updateOne({}, { $set: {...} }) ✅ 필드 일부만 수정

updateMany()

db.containerBox.updateMany(
	{name: "bear"},
	{$set: {name: "teddy bear", category: "toy"}})

document 수정 연산자

$currentDate: 오늘 날짜로 수정

$inc: 증가시켜주는 연산자

$min: 최소값으로 변경시켜주는 연산자 (그 반대 max도 있다.)

$mul: 곱해주는 연산자

$rename: 컬럼의 이름을 수정해주는 연산자

$setOnInsert: 값을 한꺼번에 설정하는 연산자

$unset: 컬럼을 제거


  • ex. 배열 데이터 생성
  • db.character.insertMany([ {name: 'x', inventory: ['pen', 'cloth', 'pen']}, {name: 'y', inventory: ['book', 'cloth']}, {name: 'z', inventory: ['wood', 'pen']} ])
  • inventory 컬럼에 있는 pen을 pencil로 수정 → (배열의 데이터 수정하는 거라 어려움)
  • db.character.updateMany( {}, {$set: {"inventory.$[penElem]": "pencil"}}, {arrayFilters: [{penElem: "pen"}]} )
  • pencil을 pen으로 수정 ⇒ 배열에서 첫 번째 요소만 변경
  • db.character.updateMany( {inventory: "pencil"}, {$set: {"inventory.$": "pen"}} )

update 파라미터와 연산자

종류

  • $addToSet: 배열 안에 해당 값이 없다면 추가하고 있다면 추가하지 않는다.
  • $pop: 배열 안의 첫 번째 혹은 마지막 요소를 삭제
  • $pull: 쿼리에 해당하는 요소 하나를 제거한다.
  • $push: 해당 요소를 배열에 추가
  • $pulAll: 해당하는 값을 가진 요소 전부를 제거한다.

Document 삭제

deleteOne(<query>, {옵션})
deleteMany(<query>, {옵션})

db.character.deleteOne({})
db.character.deleteMany({})
  • 조건을 주고 싶을 때
  • db.containerBox.deleteMany({category: "animal"})

Python과 Mongo DB 연동

  1. 필요한 패키지 설치: pymongo
  2. pip install pymongo # (만약 배포도 생각한다면 가상 환경 만들고 그 안에서 수행)
  3. 서버와 데이터베이스 연결
    • 서버 연결
      • 변수 = pymongo.MongoClient(서버IP, 포트 번호)
    • 데이터베이스 연결
      • 변수 = 서버변수.데이터베이스이름 (이때 데이터베이스가 없으면 새로 생성됨)
    • 컬렉션 연결
      • 변수 = 데이터베이스변수.컬렉션이름 (컬렉션이 없으면 새로 생성됨 but, 데이터가 들어오면 그때 실제로 생성되는 것 ex. 연결 ↔ 복사본 ↔ 원본)
    from pymongo import MongoClient
    
    # 데이터베이스 서버 연결
    con = MongoClient('127.0.0.1', 27017)
    # 데이터베이스 연결
    db = con.adam
    # collection 연결 (만들기)
    users = db.users;
    
  4. 데이터 삽입
컬렉션변수.insert_one 또는 insert_many 사용

doc1 = {'empno': 1001, 'name': '군계'} # 키를 문자열로 써야 한다.
doc2 = {'empno': 1002, 'name': '쥬니'}
doc3 = {'empno': 1003, 'name': '초콜릿'}
doc4 = {'empno': 1004, 'name': '헨리'}
doc5 = {'empno': 1005, 'name': '헨리'}

users.insert_one(doc1)
users.insert_one(doc2)
users.insert_many([doc3, doc4, doc5]) # many를 할 땐 배열로!

# print(users.count_documents({})) -> 5 (데이터 개수 확인)

→ 다시 MongoDB Compass로 가서 use adam, db.users.find() 해보면 데이터가 잘 들어간 것을 확인할 수 있다.

  1. 데이터 조회 (이건 똑같다.)
  • find() 와 find_one()
print(users.find_one({})) # 하나만 출력됨
print(users.find())

cursor = users.find()
for data in cursor:
	print(data)

728x90
반응형

'현대 오토에버 클라우드 스쿨' 카테고리의 다른 글

Linux  (0) 2025.05.20
파이썬과 데이터베이스 연동  (1) 2025.05.20
데이터베이스  (2) 2025.05.20
데이터베이스  (1) 2025.05.20
데이터베이스  (0) 2025.05.20