JangGeonWu
janggeonwu97
JangGeonWu
전체 방문자
오늘
어제
  • 분류 전체보기 (78)
    • SQLD (21)
    • 개인 공부용 (17)
    • Django (9)
    • Tableau (6)
    • ElasticSearch (8)
    • 빅데이터 엔지니어 (5)
    • Spring 퀵 스타트 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

  • 개인 공부 기록용 블로그

인기 글

최근 글

티스토리

hELLO · Designed By 정상우.
JangGeonWu
개인 공부용

mongoDB 특징과 집계 정리

mongoDB 특징과 집계 정리
개인 공부용

mongoDB 특징과 집계 정리

2022. 10. 19. 16:34

mongoDB 특징

  • SELECT를 수행할 때는 MySQL보다 느리지만, INSERT나 DELETE 그리고 UPDATE를 수행할 때는 훨씬 빠르다.

 

mongoDB 사용사례

1. 모바일 게임

  • 개발 효율 향상: 모바일 게임은 단기간에 개발해야 하는데, 게임은 특성상 변경이 자주 발생한다. 따라서, 스키마레이스인 mongoDB는 데이터 구조 변경에 유연하게 대응할 수 있어 효율이 좋다.
  • 신기능, 변경 릴리스 대응: 모바일 게임은 주 3,4회 릴리즈 하는 경우가 대다수인데, 컬럼 추가나 인덱스 추가를 온라인 상태에서 할 수 없어서 점검이 필요하게 된다. 이때 mongoDB를 사용하면 점검하지 않고 추가할 수 있다.
  • 유연한 쿼리와 인덱스: 계층 구조화한 문서 내부에서도 인덱스를 확장할 수 있고, 유연한 쿼리 검색을 할 수 있다.
  • 초기 비용이 작고, 확장성을 보장: ReplicaSets(비동기 레플리케이션)과 Sharding(자동 데이터 분산)을 제공한다.

2. 카카오 택시

  • 공간 인덱스를 활용해, 사용자 위치 기반 검색을 효율적으로 할 수 있게 함
  • 전국의 택시와 사용자 정보를 매칭시키기 위해 복합인덱스(일반 인덱스 + 공간 인덱스)를 사용해 빠른 쿼리가 가능하도록 함.

 

집계

  • 집계란, 이미 저장되어 있는 정보에서 다른 정보를 합해 출력하거나, 그룹화를 통해 다른형태로 정보를 보여주는 것을 의미한다.

도큐먼트를 '집계'하는 방법은 크게 3가지가 있다.

  1. 데이터베이스의 모든 정보를 불러와 애플리케이션 단계에서 집계하는 방법
  2. MongoDB의 맵-리듀스 기능을 이용하는 방법
  3. MongoDB의 집계 파이프라인 기능을 이용하는 방법

이를 정리한 표는 아래와 같다.

  Application Map-Reduce Pipeline
자유도 좋다 좋다 나쁘다
처리 속도 가장 나쁘다 보통 가장 좋다
램 사용량 매우 높다 높다 낮다
처리 위치 app 내부 자바스크립트 엔진 MongoDB 내부
  • 대부분의 상황에서는 파이프라인을 사용하는 것이 좋고, 파이프라인에서 처리할 수 없는 처리가 있다면 맵-리듀스 방식을 고려해보고 그 다음으로 어플리케이션 단의 처리를 고려해야 한다.

 

왜 Pipeline에서 성능이 가장 좋은건데?

'맛있는 MongoDB'라는 교재에서 이런 비유를 든다.

 

부산에서 수입한 오렌지를 이용해 오렌지 주스를 만들어 서울에서 판다고 가정해보자.
오렌지를 부산에서 서울까지 운반한 후에 오렌지 주스를 만드는 것이 좋을까, 아니면 오렌지 주스를 부산에서 만들고 완성된 제품을 서울로 운반하는 것이 좋을까?

당연히, 운반하는 무게와 부피가 줄어들기 때문에 후자가 더 좋은 선택이다.

그러니까, app에서 처리하는 건 전자고 pipeline에서 처리하는 건 후자라는 뜻이다.

 

집계 명령은 수많은 데이터를 처리해 작은 양의 정보를 애플리케이션에 전달하는 특징이 있어, 정보를 최대한 작게 만든 후에 애플리케이션으로 작아진 정보를 전송하는 것이 더 효율적이라는 것이다.

 

물론, 항상 Pipeline에서 처리하는 게 좋은건 아니다. 부산에서 만드는 것보다 서울에서 만드는 게 더 효율적인 경우도 존재하니 말이다.

 

Map-Reduce란?

맵-리듀스는 도큐먼트를 그룹으로 묶고 난 후(Mapping), 묶인 데이터에 대해서 처리(Reducing)하는 것을 기반으로 한다. 그래서 <map>과 <reduce>에 대한 정의를 꼭 해주어야 한다.

 

db.collection.mapReduce(
						<map>,
                        <reduce>,
                        {
                        out: <string or document>,
                        query: <document>,
                        sort: <document>,
                        limit: <number>,
                        finalize: <function>,
                        scope: <document>,
                        jsMode: <boolean>,
                        verbose: <boolean>               
                        }
)
파라미터 타입 설명
map function 어떤 정보들끼리 서로 묶을 수 있는지 정하는 함수
reduce function 묶인 정보들끼리 연산을 하는 함수
out string 또는 document 출력된 정보를 데이터베이스 내 기록으로 남게 설정
query document map 과정을 실행하기 전, 먼저 필요한 정보를 쿼리로 걸러냄
sort document map 과정 실행 전, 어떤 값을 기준으로 정렬할지 결정
limit number map 과정 실행 전, 함수에 넣을 도큐먼트의 수를 제한
finalize function reduce 과정 끝난 후 실행할 함수 설정
scope document map, reduce, finalize 함수에서 사용 가능한 전역변수 설정
jsMode boolean map, reduce 사이의 정보를 자바스크립트 형태로 남길 지 설정, 기본값은 false
verbose boolean 연산 처리에 걸린 시간을 보여줄 지 설정, 기본값 false

 

흰색은 선택사항, 노란색은 필수사항

뭔가 어마무시한 걸 쓴거 같은데, Map 함수 - Reducing 함수 - Out 만 잘 정의하면 된다는 걸 생각해보자.

그러면 아래와 같은 간단한 맵-리듀스 명령어를 볼 수 있다.

 

// mapping function 정의
var mapper = function(){
	emit(this."그룹핑의 기준", this."다음 단계에 넘겨줄 값")
}

// reducing function 정의, 배열의 길이를 return
// key: grouping된 기준값
// values: 배열에 grouping에 따른 각각의 값이 담긴 형태
var reducer = function(key, values){
	return values.length
}

// 콘솔창에 출력만 하라고 out를 설정
db.rating.mapReduce(mapper, reducer, {out: {inline: 1}})

여기에 원한다면 '선택 단계'에 원하는 내용을 알맞게 집어넣으면 된다.

선택 단계는 검색하는게 더 빠를듯?

 

집계 파이프라인 스테이지

앞에서 더 좋다고 한 pipeline을 나가보자. 더 효율적으로 집계 연산을 처리할 수 있게 도와주긴 하나 복잡하다는 단점이 있으니 잘 정리해야지...

 

  • 파이프라인이란 한 처리 단계의 출력이 다음 단계의 입력으로 이어지는 구조
  • 집계 파이프라인은 데이터를 받아 각각의 공정을 거쳐 원하는 결과물이 출력되는 구조로 이루어짐
스테이지 설명 형식
$project 어떤 필드를 숨기고, 어떤 필드를 새로 만들지 정함 {$project: {<field>:<boolean>}}
{$project: {<field>:<expression>}}
$group _id값으로 지정된 내용이 같은 도큐먼트끼리 그룹화 {$group: {_id: <expression>,
<field1>: {<accumlator1> : <expression1>}, ... }}
$match 도큐먼트를 필터링해서 반환한다. find문과 비슷한 역할 {$match: {<query>}}
$unwind 입력 도큐먼트에서 배열 필드를 분해해 각 요소에 대한 도큐먼트로 분리해 출력 {$unwind: <field path>}
{$unwind:{
path: <field path>,
includeArrayIndex: <string>,
preserveNullAndEmptyArrays: <boolean>
}}
$out 파이프라인의 결과를 컬렉션에 기록 {$out: "<collection name>"}

예시를 들면서, 어떤 코드인지 설명해보자.

 

1. $project 스테이지

db.rating.aggregate([
{
	$project: {_id:0, multiply: {$multiply: ["$_id", "$user_id"]}}
}
])

우선, '_id:0'은 _id 필드를 숨긴다는 것이고,

multiply는 뒤에 나올 $multiply라는 함수 결과를 나타내는 '변수명'의 개념이다.

$multiply: ["a", "b"]에 의해 '_id'값과 'user_id'값을 곱한다는 것을 알 수 있다. 저걸 왜 곱하는지는 묻지 말자, 이유 없다.

 

2. $group 스테이지

db.rating.aggregate([
{$group:{_id:"$rating", count: {$sum: 1}}}
])

우선, _id에 있는 '$rating'은 그룹화의 기준이 되는 값을 의미한다.

이 그룹화를 진행하면서, 값을 더하거나 배열로 값을 저장하는 등의 특별한 연산이 필요할 수도 있는데, 그게 바로 "count: {$sum: 1}"이다.

 

group 스테이지와 함께 쓰이는 연산자에는 first, last, max, min, avg, sum, push, addToSet같은게 있다. 대충 이런게 있구나 하고 넘어가자.

연산자명 설명
$first 첫 번째 값을 반환, $sort가 있어야 의미가 있다.
$last 마지막 값을 반환, $sort가 있어야 의미가 있다.
$max 해당 필드의 최댓값을 반환
$min 해당 필드의 최솟값을 반환
$avg  해당 필드의 평균값을 반환
$sum 해당 필드의 합산값을 반환
$push 해당 필드의 모든 값을 배열에 넣어 반환(중복 제거 X)
$addToSet $push와 같은데, 여기선 중복을 제거한다

 

3. $match 스테이지

db.rating.aggregate([
{$match: {rating: {$gte: 4}}},
{$group:{_id:"$rating", count: {$sum: 1}}}
])

그냥 원하는 쿼리를 넣으면 된다.

여기서 match는 rating이 4 이상인 경우에 대한 쿼리를 의미한다.

 

4. $unwind 스테이지

이게 좀 복잡하다. 이건 하나의 도큐먼트에 들어있는 배열 요소들을 각각의 도큐먼트에 하나의 값으로 갖도록 만드는 작업을 해준다. 그러니까, unwind 단독으로는 못쓴다는 소리라 예시를 들어야 한다.

 

어떤 pipeline의 결과가 아래와 같다고 하자.

{"_id": 5, "user_ids": [11,12,10,9]}
{"_id": 4, "user_ids": [8,4]}

여기에 아래의 unwind를 집어넣으면

{$unwind: "$user_ids"}

아래와 같은 결과가 나온다는 뜻이다.

{"_id": 5, "user_ids": 9}
{"_id": 5, "user_ids": 10}
{"_id": 5, "user_ids": 11}
{"_id": 5, "user_ids": 12}
{"_id": 4, "user_ids": 4}
{"_id": 4, "user_ids": 8}

여기에 'includeArrayIndex'를 적용시키면?

{$unwind: {path: "$user_ids", includeArrayIndex: "index"}}

아래와 같은 결과가 나오게 된다.

{"_id": 5, "user_ids": 9, "index": NumberLong(0)}
{"_id": 5, "user_ids": 10, "index": NumberLong(1)}
{"_id": 5, "user_ids": 11, "index": NumberLong(2)}
{"_id": 5, "user_ids": 12, "index": NumberLong(3)}
{"_id": 4, "user_ids": 4, "index": NumberLong(0)}
{"_id": 4, "user_ids": 8, "index": NumberLong(1)}

 

5. $out 스테이지

주어진 이름의 컬렉션에 저장하는 역할인데, 쉽게 표현하면 아래와 같다.

{$out: "user_ids_by_rating"}

파이프라인의 위의 $out 스테이지를 추가하면, 아래처럼 이 컬렉션을 사용할 수 있게 된다.

db.user_ids_by_rating.find()

일단 배운 내용은 여기까지.

확실히 안쓰던거라 정리 안하면 빨리 잊어버릴거 같긴 하다.

 

정리 끝!

 

 

'개인 공부용' 카테고리의 다른 글

MSA Design Pattern  (0) 2022.10.29
hadoop 맵리듀스 실습 기록용  (0) 2022.10.21
hadoop 실습 기록  (0) 2022.10.20
hadoop 설치(기록용)  (0) 2022.10.20
nosql, mongodb 정리  (0) 2022.10.17
  • mongoDB 특징
  • mongoDB 사용사례
  • 1. 모바일 게임
  • 2. 카카오 택시
  • 집계
  • 왜 Pipeline에서 성능이 가장 좋은건데?
  • Map-Reduce란?
  • 집계 파이프라인 스테이지
'개인 공부용' 카테고리의 다른 글
  • hadoop 맵리듀스 실습 기록용
  • hadoop 실습 기록
  • hadoop 설치(기록용)
  • nosql, mongodb 정리
JangGeonWu
JangGeonWu

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.