MongoDB 를 Sharded cluster 로 구축 후 운영하면서 수정했던 파라메터 중 하나를 공유합니다.

TaskExecutorPool은 mongos 에서 설정하는 파라메터 값 중 하나입니다.
(해당 문서는 4.4 위주로 작성 되었습니다.)

 

TaskExecutorPool?

  • Real MongoDB(235page)
  • MongoDB 라우터(mongos)는 MongoDB 클라이언트로부터 요청되는 쿼리들을 처리하기 위해서 내부적으로 서버의 CPU 코어 개수만큼 TaskExecutorPool 를 준비
  • Thread Pool 과 동일한 개념으로 생각하면 이해가 쉬움
  • MongoDB Shard 노드(Member)와의 연결 정보를 가지는 connection pool를 하나씩 가지며, Connection pool은 내부적으로 sub-connectionpool를 보유
  • 샤드 서버당 하나씩 생성
  • 만약 2개의 샤드(노드)가 존재한다면, 하나의 샤드당 connnection pool은 2개씩의 sub-connectionpool를 보유 (소스 상 specific-pool 이라고 명칭)
  • taskExecutorPoolSize 조정의 예시
    • app과 같은 서버에서 mongos가 실행된다면, app에서 많은 연결 및 작업을 한다면 TaskExecutorPool의 개수를 수동으로 증가 시키는 것도 가능
    • CPU 코어가 많은 서버라면, connection 이 너무 과도하게 생성되어 오히려 mongo shard(node)에 부하가 발생한다고 판단되면, TaskExecutorPool 개수를 제한도 가능
  • 모니터링 시 해당 수치가 높게 나오면 Sharded Clsuter 에 문제가 있기 때문에 반드시 체크가 필요 합니다.

 

taskExecutorPoolSize
  • 주어진 mongos에 사용할 Task Executor Connection Pool 의 수
  • 사용 가능한 코어 수로 default 설정 (최소 4, 최대 64)
    • 4.2 에서는 Default 가 1이며, cpu core 수에서 해당 값 만큼 설정
    • 0 으로 설정 시 자동으로 설정
    • 4.0 이전에는 core 수가 4 이하면 4, core 수가 64 이상일 경우 64로 설정
  • open connection 수를 줄이려면 낮은 값으로 설정
    • 부하 발생 시 적절하게 대처하기 힘들 수 있음.
  • default 값이 적절하며, 코어 수가 많은 서버에서는 해당 옵션을 16 이하로 설정하는 것이 좋음
  • mongos는 multiple thread pool을 유지 가능
  • 코어 수당 최대 connection 는 ShardingTaskExecutorPoolMaxSize 만큼 설정 가능
    • ex > ShardingTaskExecutorPoolMaxSize : 100 으로 설정 하고, taskExecutorPoolSize 를 4로 설정 시 최대 400개의 connection 을 생성 및 연결

Task Executor Pool

sub-ConnectionPool (SpecificPool)
  • connection pool의 connection을 얼마나 보유할지 결정
  • ShardingTaskExecutorPool로 시작
  • value 값들은 모두 3.2.11 이후부터 설정가능

 

ShardingTaskExecutorPoolHostTimeoutMS

  • mongos 가 host 와 통신하지 않아 끊는 시간(Timeout)
  • Default 300000 (5 분)
  • 시간이 길수록 피크 발생 시 유연하게 대처가 가능.
  • 일반적으로 피크 기간?의 3~4배 시간으로 설정하는 것이 tip (피크-spike 치는 시간이 가령 5분동안 지속된다면, 해당 ShardingTaskExecutorPoolHostTimeoutMS을 15~20분 으로 설정하는 것을 추천)
    • Real MongoDB에서는 30분에서 1시간 정도를 추천

 

ShardingTaskExecutorPoolMaxSize

  • taskExecutor connection pool 이 mongod(sharded) 에 대해 connection 할 수 있는 최대 개수 ( 하나의 coonection pool 이 접속할 수 있는 최대 connection 수)
  • default 값이 무제한
  • 최대 connection 수는 ShardingTaskExecutorPoolMaxSize * taskExecutorPoolSize 로 계산
  • connection floods 가 발생하면 해당 값을 제한하여 설정하는 것이 유용
    • connection floods : TCP connection floods라고 하며, 공격자가 서버에 사용 가능한 TCP connection slot 을 고갈시키는 행위 (여기서는 사용 가능한 connection 이 없을 경우를 의미)


ShardingTaskExecutorPoolMinSize

  • 각 TaskExecutor connection pool 이 sharded node에 connection 할 수 있는 최소 수
  • default 1
  • cold start 시 대기 시간 문제가 있는 경우 해당 값 증가가 도움
  • 해당 값을 증가시키면, mongos 프로세스가 ShardingTaskExecutorPoolHostTimeoutMS 가 만료될때까지 open 된 상태 유지 (TaskExecutorPoolSize * PoolMinSize * mongos 개수 per shard)
    • Real MongoDB에서는 10개 정도의 값도 충분

 

ShardingTaskExecutorPoolRefreshRequirementMS

  • connection pool 안에서 connection heartbeat로 시도할 때 wait 최대 시간
  • Default 60000 (1 분)
  • default 값을 추천
  • 해당 값을 높이면 heartbeat 트래픽이 증가하여 idle load 가 증가
  • 해당 값을 낮추면 일시적인 네트워크 오류 수를 줄일 수 있음(connection timeout 으로 인한 오류 내역을 줄일 수 있음)

 

ShardingTaskExecutorPoolRefreshTimeoutMS

  • mongos가 heartbeat timeout 을 기다리는 최대 시간
  • Default 20000 (20 초)
  • 해당 값이 낮으면 mongos가 pool 안의 connection을 유지할 수 없음
  • 네트워크 대기 시간이 긴 경우 해당 값을 늘려 네트워크 연결 유지를 향상 가능
# 설정 방법 1 (mongos config 로 등록)
sharding:
  #configDB: config_replSet/10.28.195.139:27017,10.28.195.140:27017,10.28.195.141:27017
  configDB: config_replSet/10.6.98.32:27017,10.6.102.126:27017,10.6.98.62:27017
net:
  bindIp: 0.0.0.0
  port: 27019
processManagement:
  fork: true
  pidFilePath: "/data/mongos/mongos.pid"
security:
#  authorization: enabled
  keyFile: /usr/local/mongodb/mongo_repl.key
systemLog:
  destination: file
  path: "/data/mongos/log/mongos.log"
# logAppend: true


setParameter:
  taskExecutorPoolSize: 0
# 설정 방법 2 (명령어로)
mongos> db.runCommand({setParameter:1,taskExecutorPoolSize:0})
{
    "was" : 1,
"ok" : 1,
  "$clusterTime" : {
        "clusterTime" : Timestamp(1631672362, 1),
          "signature" : {
                    "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                "keyId" : NumberLong(0)
            }
},
        "operationTime" : Timestamp(1631672362, 1)
}

#확인 방법
mongos> db.runCommand({getParameter:1,taskExecutorPoolSize:1})
{
    "taskExecutorPoolSize" : 0,
        "ok" : 1,
  "$clusterTime" : {
        "clusterTime" : Timestamp(1631672363, 1),
          "signature" : {
                    "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                "keyId" : NumberLong(0)
            }
},
        "operationTime" : Timestamp(1631672363, 1)
}

 

Connection pool 확인

  • TaskExecutorPool 개수를 확인 가능 (16개)
    • 내부에 각 Shard DB들로 추가 connection 확인 가능
mongos> db.runCommand({"connPoolStats":1})
{
        "numClientConnections" : 0,
        "numAScopedConnections" : 0,
        "totalInUse" : 1,
        "totalAvailable" : 180,
        "totalCreated" : 5871,
        "totalRefreshing" : 0,
        "replicaSetMatchingStrategy" : "matchPrimaryNode",
        "pools" : {
                "NetworkInterfaceTL-ShardRegistry" : {
                        "poolInUse" : 0,
                        "poolAvailable" : 3,
                        "poolCreated" : 41,
                        "poolRefreshing" : 0,
                        "192.168.0.126:27017" : {
                                "inUse" : 0,
                                "available" : 1,
                                "created" : 4,
                                "refreshing" : 0
                        },
                        "192.168.1.32:27017" : {
                                "inUse" : 0,
                                "available" : 1,
                                "created" : 35,
                                "refreshing" : 0
                        },
                        "192.168.1.62:27017" : {
                                "inUse" : 0,
                                "available" : 1,
                                "created" : 2,
                                "refreshing" : 0
                        }
                },
                "NetworkInterfaceTL-TaskExecutorPool-0" : {
                        "poolInUse" : 0,
                        "poolAvailable" : 9,
                        "poolCreated" : 355,
                        "poolRefreshing" : 0,
                        "192.168.0.101:27017" : {
                                "inUse" : 0,
                                "available" : 1,
                                "created" : 2,
                                "refreshing" : 0
                        },
                        "192.168.0.199:27017" : {
                        ...
                        
                   "NetworkInterfaceTL-TaskExecutorPool-1" : {
                        "poolInUse" : 1,
                        "poolAvailable" : 8,
                        "poolCreated" : 366,
                        "poolRefreshing" : 0,
                        "192.168.0.101:27017" : {
                                "inUse" : 0,
                                "available" : 1,
                                "created" : 5,
                                "refreshing" : 0
                        },
                        "192.168.0.199:27017" : {
                        ...
                  "NetworkInterfaceTL-TaskExecutorPool-10" : {
                        "poolInUse" : 0,
                        "poolAvailable" : 12,
                        "poolCreated" : 342,
                        "poolRefreshing" : 0,
                        "192.168.0.101:27017" : {
                                "inUse" : 0,
                                "available" : 1,
                                "created" : 2,
                                "refreshing" : 0
                        },
                        "192.168.0.199:27017" : {
                        ...
               "NetworkInterfaceTL-TaskExecutorPool-15" : {
                        "poolInUse" : 0,
                        "poolAvailable" : 10,
                        "poolCreated" : 349,
                        "poolRefreshing" : 0,
                        "192.168.0.101:27017" : {
                                "inUse" : 0,
                                "available" : 1,
                                "created" : 2,
                                "refreshing" : 0
                        },
                        "192.168.0.199:27017" : {
                                "inUse" : 0,
                                "available" : 1,
                                "created" : 1,
                                "refreshing" : 0
                        },
                        "192.168.0.92:27017" : {
                        ...

 

[참고]

Real MongoDB

Line Games 성세일님

AWS 이덕현님

https://www.mongodb.com/docs/v4.4/reference/parameters/#mongodb-parameter-param.taskExecutorPoolSize

https://muralidba.blogspot.com/2018/03/what-are-tunable-options-for-mongos.html

이미지 : https://www.modb.pro/db/52603

반응형

지난 블로그에서 Replica Set을 Archive 목적으로 StandAlone 으로 변경 후, 보안 이슈로 인해 Version Upgrade 도중 Local DB 관련하여 에러가 발생하여 Trouble Shooting 을 진행 하였습니다.

 

그래서 이번에는 Replica Set에서 StandAlone 으로 문제 없이 깨끗?하게 변경하는 부분에 대하여 테스트를 진행 하였습니다.

이번 테스트를 진행하면서 느낀점은, 몇몇 외국 블로그에 나온 내용들을 보면, 인증을 사용안하는 것일까 라는 의문이 생겼습니다. 왜냐하면 이번 테스트를 진행 하면서 인증(authorization) 로 인해 Local DB가 삭제가 되지 않는 이슈가 발생하였기 때문입니다.

 

글보다는 제가 진행한 방법에 대해서 이미지와 방법을 보시면 쉽게 변경 가능할 것 같습니다.

 

진행 방법

  1. Replica Set 멤버들 제거 (primary는 제거되지 않습니다. 물론 제거하는 방법도 존재-맨 하단 리플리케이션-멤버-재설정 참고)

  2. mongod.conf 에서 replication / security 모두 주석 처리 후 재 시작
  3. local DB 삭제 진행
  4. mongod.conf 에서 security 모두 원복 후 재 시작
  5. Local DB에서 startup_log collection 만 생성 되었는지 확인 및 방금 startup 로그만 존재하는 지도 확인
  6. 정상적으로 StandAlone DB 변경 완료
# 1. 멤버 삭제
# Primary 에서 진행되며, 각 멤버들만 삭제가 가능
# 기본형태 : rs.remove("host명:port")

replSet:PRIMARY> rs.remove("127.0.0.1:57018")
replSet:PRIMARY> rs.remove("127.0.0.1:57019")
----------------------------------------------------------------------------
# 2. config 수정 및 mongodb 재시작

$ vi /etc/mongod.conf

systemLog:
   destination: file
   path: /data/mongo_test/log/mongo.log
...
net:
   bindIp: 0.0.0.0
   port: 57017

#replication:
#   replSetName: "replSet"

#security:
#  authorization: enabled
#  keyFile: /data/mongo_test_repl.key


$ systemctl restart mongod.service
----------------------------------------------------------------------------
# 3. local DB 삭제 진행
$ mongo --port 57017
# 현재 standalone 으로 올라왔으며, security를 주석처리하였기 때문에 인증 없이 접속
> use local
> db.dropDatabase()

# 정상적으로 local DB가 삭제 된 것을 확인 가능
----------------------------------------------------------------------------
# 4. mongod.conf 에서 security 모두 원복 후 재시작

$ vi /etc/mongod.conf

systemLog:
   destination: file
   path: /data/mongo_test/log/mongo.log
...
net:
   bindIp: 0.0.0.0
   port: 57017

#replication:
#   replSetName: "replSet"

security:
  authorization: enabled
  keyFile: /data/mongo_test_repl.key
----------------------------------------------------------------------------
# 5. Local DB 확인 
$ mongo admin -uadmin -p --port 57017
> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
test    0.000GB
> 
> use local
switched to db local

> db.startup_log.find().pretty()

----------------------------------------------------------------------------
# 6. 완료

Replica Set 멤버 삭제

Replica Set member 들
Replica Set 멤버 제거

Stand Alone 형태의 단독 Mongodb 의 초기 모습

  • 단순 local 내에는 startup_log collection 만 존재하며, 해당 collection 에는 startup 에 대한 로그들이 존재

 

Authorization 상태에서 Local DB삭제 시 다음과 같은 에러 발생

  • 참고로, 4.2 부터는 writeConcern이 majority 라서 majority를 1로 주고 진행 하더라도 에러 발생하는 것을 확인 가능 (writeConcern의 문제가 아님)

그외

  • 일부 블로그에서는 각 collection 마다 삭제하는 것도 있었는데, 실제로 삭제가 되는 collection 이 있는가 하면, 반대로 동일하게 Auth 로 인해서 삭제가 되지 않는 collection  도 존재 하는 것을 확인

 


아래 내용은 replication 재설정 관련하여 도움을 받았던 블로그 입니다.

이 부분도 실제로 진행하였는데, 자료를 남기지 않아 추후 테스트하여 공유 하도록 하겠습니다.

출처: https://knight76.tistory.com/entry/mongodb-레플리케이션-멤버-재설정하기 [김용환 블로그(2004-2020):티스토리]

 

그 외 참고

https://9to5answer.com/how-to-convert-a-mongodb-replica-set-to-a-stand-alone-server

https://adelachao.medium.com/downgrade-mongodb-replica-set-to-a-standalone-node-fc50d58addf2

https://medium.com/@cjandsilvap/from-a-replica-set-to-a-standalone-mongodb-79fda2beaaaf

 

반응형

기존 Mongodb Upgrade 와 비슷한 문서이나, Replica set 에서 Archive 목적으로 StandAlone 으로 변경 후 Upgrade 진행 중 발생한 이슈에 대한 문서 입니다.

Replica Set using rpm upgrade (3.4 to 4.2)

 

[MongoDB] 3.4 to 4.2 upgrade (rpm)

RPM 으로 설치된 Replica Set 에 대해 Upgrade 방법 입니다. 기본적으로 Downtime 을 가지고 진행 합니다. Secondary 를 standalone 형태로 변경 후, 업그레이드 한다면 Downtime 없이 가능하며, 실제로 진행한 결..

hyunki1019.tistory.com

  • mongod.conf 에서 replica set 제거
  • 해당 상태에서 stand alone 형태로 startup 진행
  • Version Upgrade 진행

 

Repo 추가 및 설정

  • 하나의 yum Repository 로도 가능하나, 별도의 repository 추가하여 진행 하였습니다. (별다른 이유는 없습니다.)
  • 여기 까지는 위의 문서와 동일
cp /etc/yum.repos.d/mongodb-org-3.4.repo /etc/yum.repos.d/mongodb-org-3.6.repo 
$ cp /etc/yum.repos.d/mongodb-org-3.4.repo /etc/yum.repos.d/mongodb-org-4.0.repo 
$ cp /etc/yum.repos.d/mongodb-org-3.4.repo /etc/yum.repos.d/mongodb-org-4.2.repo
# 3.6 추가
$ vi /etc/yum.repos.d/mongodb-org-3.6.repo 

[mongodb-org-3.6] 
name=MongoDB Repository 
baseurl=https://repo.mongodb.org/yum/amazon/2/mongodb-org/3.6/x86_64/ 
gpgcheck=1 
enabled=1 
gpgkey=https://www.mongodb.org/static/pgp/server-3.6.asc

# 4.0 추가
$ vi /etc/yum.repos.d/mongodb-org-4.0.repo 

[mongodb-org-4.0] 
name=MongoDB Repository 
baseurl=https://repo.mongodb.org/yum/amazon/2/mongodb-org/4.0/x86_64/ 
gpgcheck=1 
enabled=1 
gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc

# 4.2 추가
$ vi /etc/yum.repos.d/mongodb-org-4.2.repo 

[mongodb-org-4.2] 
name=MongoDB Repository 
baseurl=https://repo.mongodb.org/yum/amazon/2/mongodb-org/4.2/x86_64/ 
gpgcheck=1 
enabled=1 
gpgkey=https://www.mongodb.org/static/pgp/server-4.2.asc

# yum 초기화 진행
$ yum clean all 
$ yum update list

 

MongoDB Replica Set to Standalone 

  • 단순히 conf 파일에서 replication 관련 항목을 주석 후 진행 (<- 원인)
$ vi /etc/mongod.conf 
# 아래 replication 내역 주석 처리
...
#replication:
#   replSetName: "replSet"
...

$ systemctl stop mongod.service

 

MongoDB 3.4 to 4.2 Upgrade 진행

  • 해당 문서를 보면서 upgrade 를 진행 한다면 4.0 까지만 upgrade 진행 후 아래 모든 내용의 글을 읽으신 후에 4.2 로 진행 하시기 바랍니다.!!!!!
############ 3.6 ############
# mongo 서버 종료
$ systemctl stop mongod.service

# yum 으로 update 진행
$ yum -y update mongodb-org-3.6.23

# mongodb 서버 시작
$ systemctl start mongod.service

# mongodb 접속 후 확인
$ mongo admin -uadmin -p

# 현재값 확인
> db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )

# 중요!!!# featureCompatibilityVersion 수정
> db.adminCommand( { setFeatureCompatibilityVersion: "3.6" } )

# 정상적으로 변경되었는지 확인
> db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )

############ 4.0 ############
# mongo 서버 종료
$ systemctl stop mongod.service

# yum 으로 update 진행
$ yum -y update mongodb-org-4.0.27

# mongodb 서버 시작
$ systemctl start mongod.service

# mongodb 접속 후 확인
$ mongo admin -uadmin -p

# 현재값 확인
> db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )

# 중요!!!# featureCompatibilityVersion 수정
> db.adminCommand( { setFeatureCompatibilityVersion: "4.0" } )

# 정상적으로 변경되었는지 확인
> db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )

############ 4.2 ############
# mongo 서버 종료
$ systemctl stop mongod.service

# yum 으로 update 진행
$ yum -y update mongodb-org-4.2.18

# mongodb 서버 시작
$ systemctl start mongod.service
############################### 에러 발생하면서 startup 이 안되는 이슈 발생

 

발생 이슈

  • 전제 : 기존 Replica Set에서 StandAlone 형태로 변경 후 버전 업그레이드 진행
    • cf) Replica set 에서 업그레이드 진행시 다음과 같은 문제는 발생하지 않음
  • 문제 : 4.0 에서 4.2로 업그레이드 진행 후 startup 진행 시 startup 이 되지 않는 이슈
  • 에러 내역 (로그)
    • 주요 에러 내역
      • shutting down with code:100
      • exception in initAndListen: Location40415: BSON field 'MinValidDocument.oplogDeleteFromPoint' is an unknown field., terminating
$ systemctl start mongod.service
-> error 발생하면서 startup 이 되지 않음

mongod.log

2022-07-01T17:02:59.368+0900 I  CONTROL  [main] ***** SERVER RESTARTED *****
2022-07-01T17:02:59.370+0900 I  CONTROL  [main] Automatically disabling TLS 1.0, to force-enable TLS 1.0 specify --sslDisabledProtocols 'none'
2022-07-01T17:02:59.373+0900 W  ASIO     [main] No TransportLayer configured during NetworkInterface startup
2022-07-01T17:02:59.373+0900 I  CONTROL  [initandlisten] MongoDB starting : pid=36325 port=27017 dbpath=/data/mongo/repl_0 64-bit host=192.168.0.1
2022-07-01T17:02:59.373+0900 I  CONTROL  [initandlisten] db version v4.2.21
2022-07-01T17:02:59.373+0900 I  CONTROL  [initandlisten] git version: b0aeed9445ff41af57e1f231bce990b3
2022-07-01T17:02:59.373+0900 I  CONTROL  [initandlisten] OpenSSL version: OpenSSL 1.0.2k-fips  26 Jan 2017
2022-07-01T17:02:59.373+0900 I  CONTROL  [initandlisten] allocator: tcmalloc
2022-07-01T17:02:59.373+0900 I  CONTROL  [initandlisten] modules: none
2022-07-01T17:02:59.373+0900 I  CONTROL  [initandlisten] build environment:
2022-07-01T17:02:59.373+0900 I  CONTROL  [initandlisten]     distmod: amazon2
2022-07-01T17:02:59.373+0900 I  CONTROL  [initandlisten]     distarch: x86_64
2022-07-01T17:02:59.373+0900 I  CONTROL  [initandlisten]     target_arch: x86_64
2022-07-01T17:02:59.373+0900 I  CONTROL  [initandlisten] options: { config: "/etc/mongod.conf", net: { bindIp: "0.0.0.0", port: 27017 }, processManagement: { fork: true, pidFilePath: "/var/run/mongodb/mongod.pid" }, security: { authorization: "enabled" }, storage: { dbPath: "/data/mongo/repl_0", journal: { enabled: true } }, systemLog: { destination: "file", logAppend: true, path: "/var/log/mongodb/mongod.log", quiet: true } }
2022-07-01T17:02:59.373+0900 I  STORAGE  [initandlisten] Detected data files in /data/mongo/repl_0 created by the 'wiredTiger' storage engine, so setting the active storage engine to 'wiredTiger'.
2022-07-01T17:02:59.373+0900 I  STORAGE  [initandlisten] wiredtiger_open config: create,cache_size=7360M,cache_overflow=(file_max=0M),session_max=33000,eviction=(threads_min=4,threads_max=4),config_base=false,statistics=(fast),log=(enabled=true,archive=true,path=journal,compressor=snappy),file_manager=(close_idle_time=100000,close_scan_interval=10,close_handle_minimum=250),statistics_log=(wait=0),verbose=[recovery_progress,checkpoint_progress],
2022-07-01T17:02:59.914+0900 I  STORAGE  [initandlisten] WiredTiger message [1656662579:914502][36325:0x7ff6d143a080], txn-recover: Recovering log 71644 through 71645
2022-07-01T17:02:59.985+0900 I  STORAGE  [initandlisten] WiredTiger message [1656662579:985670][36325:0x7ff6d143a080], txn-recover: Recovering log 71645 through 71645
...
2022-07-01T17:03:00.247+0900 I  STORAGE  [initandlisten] WiredTiger message [1656662580:247926][36325:0x7ff6d143a080], txn-recover: Set global recovery timestamp: (0, 0)
2022-07-01T17:03:00.258+0900 I  RECOVERY [initandlisten] WiredTiger recoveryTimestamp. Ts: Timestamp(0, 0)
2022-07-01T17:03:00.260+0900 I  STORAGE  [initandlisten] No table logging settings modifications are required for existing WiredTiger tables. Logging enabled? 1
2022-07-01T17:03:00.267+0900 I  STORAGE  [initandlisten] Starting OplogTruncaterThread local.oplog.rs
2022-07-01T17:03:00.267+0900 I  STORAGE  [initandlisten] The size storer reports that the oplog contains 44774827 records totaling to 53269933556 bytes
2022-07-01T17:03:00.267+0900 I  STORAGE  [initandlisten] Sampling the oplog to determine where to place markers for truncation
2022-07-01T17:03:00.269+0900 I  STORAGE  [initandlisten] Sampling from the oplog between Jun 12 09:53:56:57 and Jun 30 10:45:36:1 to determine where to place markers for truncation
2022-07-01T17:03:00.269+0900 I  STORAGE  [initandlisten] Taking 992 samples and assuming that each section of oplog contains approximately 451255 records totaling to 536871395 bytes
2022-07-01T17:03:00.743+0900 I  STORAGE  [initandlisten] Placing a marker at optime Jun 12 13:10:49:4
2022-07-01T17:03:00.743+0900 I  STORAGE  [initandlisten] Placing a marker at optime Jun 12 17:58:42:4
....
2022-07-01T17:03:00.743+0900 I  STORAGE  [initandlisten] Placing a marker at optime Jun 30 08:04:48:175
2022-07-01T17:03:00.743+0900 I  STORAGE  [initandlisten] WiredTiger record store oplog processing took 475ms
2022-07-01T17:03:00.744+0900 I  STORAGE  [initandlisten] Timestamp monitor starting
2022-07-01T17:03:00.771+0900 I  STORAGE  [initandlisten] exception in initAndListen: Location40415: BSON field 'MinValidDocument.oplogDeleteFromPoint' is an unknown field., terminating
2022-07-01T17:03:00.771+0900 I  REPL     [initandlisten] Stepping down the ReplicationCoordinator for shutdown, waitTime: 10000ms
2022-07-01T17:03:00.771+0900 I  SHARDING [initandlisten] Shutting down the WaitForMajorityService
2022-07-01T17:03:00.771+0900 I  NETWORK  [initandlisten] shutdown: going to close listening sockets...
2022-07-01T17:03:00.771+0900 I  NETWORK  [initandlisten] Shutting down the global connection pool
2022-07-01T17:03:00.771+0900 I  STORAGE  [initandlisten] Shutting down the FlowControlTicketholder
2022-07-01T17:03:00.771+0900 I  -        [initandlisten] Stopping further Flow Control ticket acquisitions.
2022-07-01T17:03:00.771+0900 I  STORAGE  [initandlisten] Shutting down the PeriodicThreadToAbortExpiredTransactions
2022-07-01T17:03:00.771+0900 I  STORAGE  [initandlisten] Shutting down the PeriodicThreadToDecreaseSnapshotHistoryIfNotNeeded
2022-07-01T17:03:00.771+0900 I  REPL     [initandlisten] Shutting down the ReplicationCoordinator
2022-07-01T17:03:00.771+0900 I  SHARDING [initandlisten] Shutting down the ShardingInitializationMongoD
2022-07-01T17:03:00.771+0900 I  REPL     [initandlisten] Enqueuing the ReplicationStateTransitionLock for shutdown
2022-07-01T17:03:00.771+0900 I  -        [initandlisten] Killing all operations for shutdown
...
2022-07-01T17:03:01.196+0900 I  STORAGE  [initandlisten] WiredTiger message [1656662581:196670][36325:0x7ff6d143a080], txn-recover: Set global recovery timestamp: (0, 0)
2022-07-01T17:03:01.335+0900 I  STORAGE  [initandlisten] shutdown: removing fs lock...
2022-07-01T17:03:01.335+0900 I  -        [initandlisten] Dropping the scope cache for shutdown
2022-07-01T17:03:01.335+0900 I  CONTROL  [initandlisten] now exiting
2022-07-01T17:03:01.335+0900 I  CONTROL  [initandlisten] shutting down with code:100

 

해결 방안

  • 4.0 Downgrade 진행 및 local DB의 replset.minvalid 컬렉션의 oplogDeleteFromPoint 필드 unset 진행
# 4.0 Downgrade 진행
$ yum -y downgrade mongodb-org-4.0.27

# mongodb startup 진행
$ systemctl start mongod.service

# mongodb 접속 후 확인
$ mongo admin -uadmin -p

# local 에서 replset.minvalid Collection 의 oplogDeleteFromPoint 필드 $unset 진행
> use local
switched to db local

> db.replset.minvalid.find({}).pretty ();
{
        "_id" : ObjectId("5ec3b7c56db7b066b3f5d5e3"),
        "ts" : Timestamp(1643225280, 1),
        "t" : NumberLong(7),
        "oplogDeleteFromPoint" : Timestamp(0, 0)
}

# update 진행 검색 조건은 _id 결과로 $unset 진행
> db.replset.minvalid.update(
...  { "_id" : ObjectId("5ec3b7c56db7b066b3f5d5e3")},
...    { $unset: { oplogDeleteFromPoint: ""} }
... );
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

# 해당 필드 값 확인
> db.replset.minvalid.find({}).pretty ();
{
        "_id" : ObjectId("5ec3b7c56db7b066b3f5d5e3"),
        "ts" : Timestamp(1643225280, 1),
        "t" : NumberLong(7)
}
> exit

# 이후 다시 mongodb 4.2 upgrade 진행
# mongo 서버 종료
$systemctl stop mongod.service

# yum 으로 update 진행
yum -y update mongodb-org-4.2.18

# mongodb 서버 시작
$ systemctl start mongod.service

# mongodb 접속 후 확인
$ mongo admin -uadmin -p

# 현재값 확인
> db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )

# 중요!!!# featureCompatibilityVersion 수정
> db.adminCommand( { setFeatureCompatibilityVersion: "4.2" } )

# 정상적으로 변경되었는지 확인
> db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )

 

발생 원인

  • Replica set 을 정상적으로 삭제 하지 않고 단순 config 파일에서 수정 후 진행하다가 발생한 이유
  • mongodb 4.2에서 MinValidDocument.oplogDeleteFromPoint dependency (예상)

 

발생 방지 방법

  • MongoDB Replica set 에서 StandAlone 으로 변경 시 정상적인 방법으로 진행
# 1. 모든 Secondary 삭제 진행
repl_set:PRIMARY> rs.remove("Secondary 호스트 이름:27017")
repl_set:PRIMARY> rs.remove("Arbiter 호스트 이름:27018")

# 2. config 파일에서 replication 주석 처리 또는 제거
$ vi /etc/mongod.conf
...
#replication:
#   replSetName: "replSet"
...

# 3. mongodb 재시작(Primary)
$ systemctl restart mongod.service

# 4. local database 삭제
# mongodb 접속 시 현재 standalone 상태
> use local
> db.dropDatabase()

# 5. mongodb 깔끔하게 확인차 재시작(Primary)
$ systemctl restart mongod.service

 

결론

  • Replica Set의 동작 방식 뿐만 아니라 각 DB(Local, admin 등)들의 역할에 대해서 다시 한번 공부가 필요
  • MongoDB에 대한 공부 공부 공부!!!

 

해당 문제를 해결하는 과정에 많은 도움을 주신 TechPM 박경배 팀장님에게 감사 인사 드립니다.

반응형
  • RPM 으로 설치된 Replica Set 에 대해 Upgrade 방법 입니다.
  • 기본적으로 Downtime 을 가지고 진행 합니다.
    • Secondary 를 standalone 형태로 변경 후, 업그레이드 한다면 Downtime 없이 가능하며, 실제로 진행한 결과 문제 없이 업그레이드 진행 하였습니다. 
    • 이 부분에 대해서는 별도로 문서는 만들지 않았습니다. 
  • PSA 구조로 진행하였으며, PSS 구조로 진행 한다면 Arbiter 업그레이드 대신 Secondary 진행한 그대로 대신 진행 하면 됩니다.
  • MongoDB 3.4 to 4.2 로 진행 합니다.
  • aws ec2 - amazon2 에서 진행 하였습니다.

 

Repo 추가 및 설정

  • 하나의 yum Repository 로도 가능하나, 별도의 repository 추가하여 진행 하였습니다. (별다른 이유는 없습니다.)
cp /etc/yum.repos.d/mongodb-org-3.4.repo /etc/yum.repos.d/mongodb-org-3.6.repo 
$ cp /etc/yum.repos.d/mongodb-org-3.4.repo /etc/yum.repos.d/mongodb-org-4.0.repo 
$ cp /etc/yum.repos.d/mongodb-org-3.4.repo /etc/yum.repos.d/mongodb-org-4.2.repo
# 3.6 추가
$ vi /etc/yum.repos.d/mongodb-org-3.6.repo 

[mongodb-org-3.6] 
name=MongoDB Repository 
baseurl=https://repo.mongodb.org/yum/amazon/2/mongodb-org/3.6/x86_64/ 
gpgcheck=1 
enabled=1 
gpgkey=https://www.mongodb.org/static/pgp/server-3.6.asc

# 4.0 추가
$ vi /etc/yum.repos.d/mongodb-org-4.0.repo 

[mongodb-org-4.0] 
name=MongoDB Repository 
baseurl=https://repo.mongodb.org/yum/amazon/2/mongodb-org/4.0/x86_64/ 
gpgcheck=1 
enabled=1 
gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc

# 4.2 추가
$ vi /etc/yum.repos.d/mongodb-org-4.2.repo 

[mongodb-org-4.2] 
name=MongoDB Repository 
baseurl=https://repo.mongodb.org/yum/amazon/2/mongodb-org/4.2/x86_64/ 
gpgcheck=1 
enabled=1 
gpgkey=https://www.mongodb.org/static/pgp/server-4.2.asc

# yum 초기화 진행
$ yum clean all 
$ yum update list

 

MongoDB 3.6 Upgrade 진행

  • Secondary -> Arbiter -> Primary 순으로 진행 합니다.
############ Secondary ############
# mongo 서버 종료 
$ systemctl stop mongod.service 

# yum 으로 update 진행 
$ yum update mongodb-org-3.6.23 

# mongodb 서버 시작 
$ systemctl start mongod.service 

# mongodb 접속 후 확인 
$ mongo admin -uadmin -p 

# replica 접속 되었는지 확인 
repl_set:SECONDARY> rs.status()

############ Arbiter ############
# mongo 서버 종료 
$ systemctl stop mongod_arb.service 

# yum 으로 update 진행 
$ yum update mongodb-org-3.6.23 

# mongodb 서버 시작 
$ systemctl start mongod_arb.service 

# mongodb 접속 후 확인 
$ mongo --port 27018 

# replica 접속 되었는지 확인 
repl_set:ARBITER> rs.status()

############ Primary ############
# mongo 서버 종료 
$ systemctl stop mongod.service 

# yum 으로 update 진행 
$ yum update mongodb-org-3.6.23 

# mongodb 서버 시작 
$ systemctl start mongod.service 

# mongodb 접속 후 확인 
$ mongo admin -uadmin -p 
# replica 접속 되었는지 확인 
repl_set:PRIMARY> rs.status() 

############ 중요!!! ############
# featureCompatibilityVersion 수정 
repl_set:PRIMARY> db.adminCommand( { setFeatureCompatibilityVersion: "3.6" } ) 
# 정상적으로 변경되었는지 확인 
repl_set:PRIMARY> db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )

 

 

MongoDB 4.0 Upgrade 진행

  • Secondary -> Arbiter -> Primary 순으로 진행 합니다.
############ Secondary ############
# mongo 서버 종료 
$ systemctl stop mongod.service 

# yum 으로 update 진행 
$ yum update mongodb-org-4.0.27 

# mongodb 서버 시작 
$ systemctl start mongod.service 

# mongodb 접속 후 확인 
$ mongo admin -uadmin -p 

# replica 접속 되었는지 확인 
repl_set:SECONDARY> rs.status()

############ Arbiter ############
# mongo 서버 종료 
$ systemctl stop mongod_arb.service 

# yum 으로 update 진행 
$ yum update mongodb-org-4.0.27 

# mongodb 서버 시작 
$ systemctl start mongod_arb.service 

# mongodb 접속 후 확인 
$ mongo --port 27018 

# replica 접속 되었는지 확인 
repl_set:ARBITER> rs.status()

############ Primary ############
# mongo 서버 종료 
$ systemctl stop mongod.service 

# yum 으로 update 진행 
$ yum update mongodb-org-4.0.27 

# mongodb 서버 시작 
$ systemctl start mongod.service 

# mongodb 접속 후 확인 
$ mongo admin -uadmin -p 
# replica 접속 되었는지 확인 
repl_set:PRIMARY> rs.status() 

############ 중요!!! ############
# featureCompatibilityVersion 수정 
repl_set:PRIMARY> db.adminCommand( { setFeatureCompatibilityVersion: "4.0" } ) 
# 정상적으로 변경되었는지 확인 
repl_set:PRIMARY> db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )

 

MongoDB 4.2 Upgrade 진행

  • Secondary -> Arbiter -> Primary 순으로 진행 합니다.
############ Secondary ############
# mongo 서버 종료 
$ systemctl stop mongod.service 

# yum 으로 update 진행 
$ yum update mongodb-org-4.2.18

# mongodb 서버 시작 
$ systemctl start mongod.service 

# mongodb 접속 후 확인 
$ mongo admin -uadmin -p 

# replica 접속 되었는지 확인 
repl_set:SECONDARY> rs.status()

############ Arbiter ############
# mongo 서버 종료 
$ systemctl stop mongod_arb.service 

# yum 으로 update 진행 
$ yum update mongodb-org-4.2.18

# mongodb 서버 시작 
$ systemctl start mongod_arb.service 

# mongodb 접속 후 확인 
$ mongo --port 27018 

# replica 접속 되었는지 확인 
repl_set:ARBITER> rs.status()

############ Primary ############
# mongo 서버 종료 
$ systemctl stop mongod.service 

# yum 으로 update 진행 
$ yum update mongodb-org-4.2.18

# mongodb 서버 시작 
$ systemctl start mongod.service 

# mongodb 접속 후 확인 
$ mongo admin -uadmin -p 
# replica 접속 되었는지 확인 
repl_set:PRIMARY> rs.status() 

############ 중요!!! ############
# featureCompatibilityVersion 수정 
repl_set:PRIMARY> db.adminCommand( { setFeatureCompatibilityVersion: "4.2" } ) 
# 정상적으로 변경되었는지 확인 
repl_set:PRIMARY> db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )

 

  • 반복 작업이지만, MongoDB는 순차적으로 upgrade 하는 것이 권고 사항 입니다.
  • 또한, featureCompatibilityVersion 을 수정하지 않는 경우 다음 version upgrade 이후 startup 이 되지 않는 이슈가 발생 합니다.
  • 이 때, 이전 version으로 downgrade 이 후, featureCompatibilityVersion 수정 후 다시 업그레이드를 하게 되면 진행이 가능합니다.
    • 모든 upgrade 작업은 백업은 필수! 입니다.
    • 가령, 문제가 발생하여 repair 명령어를 이용하여 startup 을 하게 되면, 모든 collection repair 및 index rebuild 까지 진행 하기 때문에 데이터 size 가 작은 사이트에서는 크게 이슈가 발생하지 않겠지만, 저희처럼 테라바이트 단위라면 repair 시간을 예측 하기 힘든 상황이 발생 합니다. (6테라 size의 db를 repair 로 만 하루 넘어가도 끝나지 않아 포기하고 기존 ec2 snapshot 으로 복구 진행 하였습니다.)
  • 이후, 다른 글에서 upgrade 과정에서 MinValidDocument.oplogDeleteFromPoint is an unknown field 이슈에 대해 해결 방법을 공유 하도록 하겠습니다.
반응형

샘플 데이터

  • "맛있는 MongoDB"
    • 샘플 데이터 공유
$ git clone https://github.com/Karoid/mongodb_tutorials.git
$ ec2-user@mongodb:~$ cd mongodb_tutorials/car_accident/

ec2-user@mongodb:~/mongodb_tutorials/car_accident$ mongoimport -d car_accident -c area --file area.json
2021-03-02T13:29:00.380+0000    connected to: localhost
2021-03-02T13:29:00.403+0000    imported 228 documents
ec2-user@mongodb:~/mongodb_tutorials/car_accident$
ec2-user@mongodb:~/mongodb_tutorials/car_accident$ mongoimport -d car_accident -c by_month --file by_month.json
2021-03-02T13:29:32.252+0000    connected to: localhost
2021-03-02T13:29:32.309+0000    imported 227 documents
ec2-user@mongodb:~/mongodb_tutorials/car_accident$
ec2-user@mongodb:~/mongodb_tutorials/car_accident$ mongoimport -d car_accident -c by_road_type --file by_road_type.json
2021-03-02T13:29:50.266+0000    connected to: localhost
2021-03-02T13:29:50.309+0000    imported 227 documents
ec2-user@mongodb:~/mongodb_tutorials/car_accident$
ec2-user@mongodb:~/mongodb_tutorials/car_accident$ mongoimport -d car_accident -c by_type --file by_type.json
2021-03-02T13:30:08.372+0000    connected to: localhost
2021-03-02T13:30:08.408+0000    imported 687 documents

> show dbs
admin        0.000GB
car_accident  0.000GB
config        0.000GB
local        0.000GB
test          0.000GB
>
> use car_accident
switched to db car_accident
> show tables
area
by_month
by_road_type
by_type

 

  • Find 관련 명령어에는 여러 개가 있으며, 그 중에 많이 사용하는 위주로 가이드 하며, 필요 시 추가 가이드 진행 예정

명령어

내역

find()

검색

findAndModify()

검색 후 수정

Update, upset, remove 모두 가능

new : true를 설정하여 update 이후 값을 리턴

new : false 또는 미적용 시 update 이전 값을 리턴

 

db.monsters.findAndModify({

    query: { name: "Dragon" },

    update: { $inc: { att: 1000 } ,$set :{"name":"Dragon","hp":4000,"att":1000}},

    upsert: true,

    new : true

})

findOne()

한건만 검색

findOneAndDelete()

한건만 검색 후 삭제

findOneAndReplace() > v3.2

한건만 검색 후 변경

returnNewDocument : true 설정하여 변경 전후 확인 가능

Replace 와 Update의 경우 Update는 명시한 필드만 변경 되지만, Replace의 경우는 명시한 필드 변경 외에는 나머지 필드는 모두 삭제 됨

가급적이면 Update만 사용 해야함

findOneAndUpdate()  > v3.2

한건만 검색 후 변경

returnNewDocument : true 설정하여 변경 전후 확인 가능

 

[Find]

  • Find명령어 사용 시 필요한 filed 명을 사용하여 검색하시기 바랍니다.(Covered Query)
  • Ex) db.bios.find( {조건}, {_id:0, name:1 , money:1})
    • 쿼리가 요구하는(리턴되는) 내용의 모든 필드가 하나의 Index에 포함되어 있으므로, Index만 조회하기 때문에 Document를 조회하는 것보다 훨씬 빠름(일반적으로 인덱스 키는 RAM에 적재되 있거나 디스크에 순차적으로 위치 하기 때문)
    • _id 필드를 0으로 명시하지 않는 경우, 결과 값에서 표현이 되기 때문에, covered query 조건이 되지 않으므로, 반드시 _id 필드를 0으로  명시
    • 쿼리에 필드가 없거나 필드의 값이 null 이 되면 안된다. i.e. {"field" : null} or {"field" : {$eq : null}}
    • v3.6 부터 embedded document의 경우도 적용 (이전 버전 에서는 불가능?)
      • db.userdata.find( { "user.login": "tester" }, { "user.login": 1, _id: 0 } )
      • 테스트 결과 안되요ㅠ(알고 계신 분 알려주세요)
    • 제한
      • Geospatial Index 에서는 사용 못함
      • Multikey Index에서 배열 필드에 대해서는 Covered index 가 불가능(v3.6부터 non-array field들에 대해서는 사용 가능)
    • Covered Query
  • field 명시 방법
    • 필드에 _id 여부는 항상 명시해야 함(_id를 쿼리 결과 내에서 표현하거나 표현하지 않는 것에 대해 명시를 반드시 해야 함)
    • _id에 대해서만 혼용 사용 가능하며, 다른 필드들에 대해서는 보여 주고 싶은 것에 대해서만 1로 명시, 0으로 명시하면 에러 발생
    • Ex ) > db.thing.find( { }, {_id:0, empno: 1} )  // empno만 표시하고, _id 및 다른 필드는 표시 안함. // 여기서 중요한건 _id는 항상 명시해 줘야 하며, 보고 싶은 필드만 1로 설정해서 표시 // 다른 필드의 경우 birth 필드가 있더라도 birth:0 으로 하면 에러 발생...왜???모르겠음
    • 만약 empno만 빼고 다 보고자 하면 그때는 > db.thing.find( { }, {empno: 0} ) 이런 식으로 표시
    • 보고 싶은 field가 있다면, field를 명시할 때 보겠다는 field 들만 명시를 하고,field 명시 여부를 혼용해서 명시하게 되면, 에러가 발생
    • 반대로 보고 싶지 않은 field가 있다면 명시 안하겠다는 field 들만 명시를 해야 함

[Filed] 

  • Covered Query Test
# semester 에 대한 Index 확인
> db.employee.getIndexes()
[
        {
                "v" : 2,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_"
        },
        {
                "v" : 2,
                "key" : {
                        "semester" : 1
                },
                "name" : "semester_1"
        }
]

# _id 필드를 명시적으로 0 처리 하지 않으면, 자연스럽게 _id 필드도 같이 표시되기 때문에 명시적으로 _id 필드를 표시 안한다고 해야, Convered query 적용 여부를 확인 가능
> db.employee.find({semester:3},{_id:0, semester:1}).explain()

# 필드를 표시 안하게 되는 경우 _id 필드가 표현 되기에 covered query가 되지 않음
> db.employee.find({semester:3},{"field" : null}).explain()

#  covered index 확인

# 필드를 표시 안 하게 되는 경우 _id 필드가 표현 되기에 covered query가 되지 않음 (단순 인덱스 스캔 - fetch 진행)

#실패 - embedded Document 에 대한 covered query 실패
> db.inventory.insertMany( [
    { item: "journal", instock: [ { warehouse: "A", qty: 5 }, { warehouse: "C", qty: 15 } ] },
    { item: "notebook", instock: [ { warehouse: "C", qty: 5 } ] },
    { item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 15 } ] },
    { item: "planner", instock: [ { warehouse: "A", qty: 40 }, { warehouse: "B", qty: 5 } ] },
    { item: "postcard", instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);
{
        "acknowledged" : true,
        "insertedIds" : [
                ObjectId("603cf504850313ab26e27ced"),
                ObjectId("603cf504850313ab26e27cee"),
                ObjectId("603cf504850313ab26e27cef"),
                ObjectId("603cf504850313ab26e27cf0"),
                ObjectId("603cf504850313ab26e27cf1")
        ]
}
>
> db.inventory.createIndex({item:1, "instock.qty":1})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}
# 데이터 확인
> db.inventory.find({item:"journal", "instock.qty":5}, {_id:0,item:1, "instock.qty":1})
{ "item" : "journal", "instock" : [ { "qty" : 5 }, { "qty" : 15 } ] }

# covered 체크
> db.inventory.find({item:"journal", "instock.qty":5}, {_id:0,item:1, "instock.qty":1}).explain()

# 단일로 진행 실패
> db.inventory.insertOne({ item: "test_item", instock: [{warehouse: "A", qty: 5}]})
> db.inventory.find({item:"test_item", "instock.qty":5}, {_id:0,item:1, "instock.qty":1}).explain()

# 단일로 진행 실패 2
> db.inventory.insertOne({ item: "test_item_2", instock: [{qty: 5}]})
> db.inventory.find({item:"test_item_2", "instock.qty":5}, {_id:0,item:1, "instock.qty":1}).explain()

비교(Comparison) 연산자

operator

설명

 

$eq

(equals) 주어진 값과 일치하는 값

find({ 대상필드:{원하는연산자:조건값}})

 

 

 

 

 

$gt

(greater than) 주어진 값보다 큰 값

$gte

(greather than or equals) 주어진 값보다 크거나 같은 값

$lt

(less than) 주어진 값보다 작은 값

$lte

(less than or equals) 주어진 값보다 작거나 같은 값

$ne

(not equal) 주어진 값과 일치하지 않는 값

$in

주어진 배열 안에 속하는 값

반드시 배열 형태로 질의

find({필드:{$in / $nin :[ { 원하는값 A } , { 원하는값 B } ] } })

$nin

주어진 배열 안에 속하지 않는 값

논리 연산자

operator

설명

 

$or

주어진 조건중 하나라도 true 일 때 true

배열 형태로 질의 진행

find({$or / $and / $not / $nor : [{조건A},{조건B}]})

$and

주어진 모든 조건이 true 일 때 true

$not

주어진 조건이 false 일 때 true

$nor

주어진 모든 조건이 false 일때 true

> db.inventory.find({"instock.qty":{$eq:5}})
{ "_id" : ObjectId("603cf504850313ab26e27ced"), "item" : "journal", "instock" : [ { "warehouse" : "A", "qty" : 5 }, { "warehouse" : "C", "qty" : 15 } ] }
{ "_id" : ObjectId("603cf504850313ab26e27cee"), "item" : "notebook", "instock" : [ { "warehouse" : "C", "qty" : 5 } ] }
{ "_id" : ObjectId("603cf504850313ab26e27cf0"), "item" : "planner", "instock" : [ { "warehouse" : "A", "qty" : 40 }, { "warehouse" : "B", "qty" : 5 } ] }
{ "_id" : ObjectId("603cf675850313ab26e27cf2"), "item" : "test_item", "instock" : [ { "warehouse" : "A", "qty" : 5 } ] }
{ "_id" : ObjectId("603cf6d7850313ab26e27cf3"), "item" : "test_item_2", "instock" : [ { "qty" : 5 } ] }

# county 가 종로구,중구 또는 population 이 3,552,490 보다 큰 경우
> db.area.find({$or:[{county: "종로구"},{county:"중구"},{population:{$gte:3552490}}]})

{ "_id" : ObjectId("5c88f9f70da47a8507752775"), "city_or_province" : "서울", "county" : "종로구", "population" : 152737 }
{ "_id" : ObjectId("5c88f9f70da47a8507752776"), "city_or_province" : "서울", "county" : "중구", "population" : 125249 }
{ "_id" : ObjectId("5c88f9f70da47a850775278e"), "city_or_province" : "부산", "county" : "중구", "population" : 45208 }
{ "_id" : ObjectId("5c88f9f70da47a8507752830"), "city_or_province" : "대구", "county" : "중구", "population" : 79712 }
{ "_id" : ObjectId("5c88f9f70da47a850775283f"), "city_or_province" : "인천", "county" : "중구", "population" : 115249 }
{ "_id" : ObjectId("5c88f9f70da47a8507752848"), "city_or_province" : "대전", "county" : "중구", "population" : 252490 }
{ "_id" : ObjectId("5c88f9f70da47a850775284c"), "city_or_province" : "울산", "county" : "중구", "population" : 242536 }

# county 가 종로구,중구 가운데, population 이 125,249 보다 큰 경우 (and / or 연산이 공존)
> db.area.find({$and:[{$or:[{county: "종로구"},{county:"중구"}]},{population:{$gte:125249}}] })

{ "_id" : ObjectId("5c88f9f70da47a8507752775"), "city_or_province" : "서울", "county" : "종로구", "population" : 152737 }
{ "_id" : ObjectId("5c88f9f70da47a8507752776"), "city_or_province" : "서울", "county" : "중구", "population" : 125249 }
{ "_id" : ObjectId("5c88f9f70da47a8507752848"), "city_or_province" : "대전", "county" : "중구", "population" : 252490 }
{ "_id" : ObjectId("5c88f9f70da47a850775284c"), "city_or_province" : "울산", "county" : "중구", "population" : 242536 }

# county 가 종로구,중구 가운데, population 이 125,249 보다 큰 경우 ($and / $in 연산이 공존)
> db.area.find({$and:[{county:{$in:["종로구","중구"]}}, {population:{$gte:125249}}] })

{ "_id" : ObjectId("5c88f9f70da47a8507752775"), "city_or_province" : "서울", "county" : "종로구", "population" : 152737 }
{ "_id" : ObjectId("5c88f9f70da47a8507752776"), "city_or_province" : "서울", "county" : "중구", "population" : 125249 }
{ "_id" : ObjectId("5c88f9f70da47a8507752848"), "city_or_province" : "대전", "county" : "중구", "population" : 252490 }
{ "_id" : ObjectId("5c88f9f70da47a850775284c"), "city_or_province" : "울산", "county" : "중구", "population" : 242536 }

$regex 연산자

  • $regex 연산자(정규표현식)를 이용하여, Document를  찾을 수 있음
{ <field>: { $regex: /pattern/, $options: '<options>' } }
{ <field>: { $regex: 'pattern', $options: '<options>' } }
{ <field>: { $regex: /pattern/<options> } }
{ <field>: /pattern/<options> }
  • 4번쨰 라인 처럼 $regex 를 작성하지 않고 바로 정규식을 쓸 수도 있으며,  $options 정보 를 이용도 가능

option

설명

i

대소문자 무시

m

정규식에서 anchor(^) 를 사용 할 때 값에 \n 이 있다면 무력화

x

정규식 안에있는 whitespace를 모두 무시

s

dot (.) 사용 할 떄 \n 을 포함해서 매치

  • 정규식 test_item_[1-2] 에 일치하는 값이 item 조회
    • 조회하는 데이터의 "" 사용 안함.
> db.inventory.find({item : /test_item_[1-2]/})
{ "_id" : ObjectId("603cf6d7850313ab26e27cf3"), "item" : "test_item_2", "instock" : [ { "qty" : 5 } ] }

#대소문자 무시 i 옵션 이용
> db.inventory.find({item : /Test_item_[1-2]/i})
{ "_id" : ObjectId("603cf6d7850313ab26e27cf3"), "item" : "test_item_2", "instock" : [ { "qty" : 5 } ] }

$text 연산자

  • Text 검색으로 단어 형태만 검색이 가능 (띄어쓰기 단위 -> 가령 한 단어 내에 포함된 것은 검색이 안됨)
  • 대 소문자 구분 안함
  • 각 나라별 언어에 맞춰 검색이 지원되고 있지만, 한글은 지원되지 않음.
  • 문자열 인덱스를 만들어야 사용 가능
    • 해당 Collection 의 텍스트 인덱스 안에서만 작동하기 때문

필드

설명

$search

검색하려는 내용을 담는다. 구절로 설정되지 않으면 띄어 쓴 단어를 포함한 모든 Document반환

$language

Option. 검색하는 언어를 설정

MongoDB가 지원하는 언어를 설정할 수 있으며, 설정되지 않으면 인덱스에 설정된 내용을 따름

$caseSensitive

Option. Bollean 값.

문자의 대,소문자 구분을 결정하며, Default는 구분하지 않음(False)

$diacriticSensitive

Option. Bollean 값.

e`와 e 와 같이 알파벳의 위아래에 붙이는 기호를 무시할지를 정함. Default로는 false (무시 하지 않음)

> db.inventory.createIndex({ item:"text" })

> db.inventory.find( {$text:{$search:"test"}} )
{ "_id" : ObjectId("603e498748883f8cbbc18bd9"), "item" : "keep Test" }
{ "_id" : ObjectId("603e47f148883f8cbbc18bd7"), "item" : "keep test" }
{ "_id" : ObjectId("603e47e248883f8cbbc18bd6"), "item" : "test keep" }

  • 문자열 내 test 를 검색하는데, test_item 같은 것은 검색이 안됨. keep test / test keep 같이 띄어쓰기 된 것에 대해서만 검색

$where 연산자

  • $where 연산자를 통하여 javascript expression 을 사용 가능
#comments field 가 비어있는 Document 조회

> db.articles.find( { $where: "this.comments.length == 0" } )
{ "_id" : ObjectId("56c0ab6c639be5292edab0c4"), "title" : "article01", "content" : "content01", "writer" : "Velopert", "likes" : 0, "comments" : [ ]

 

$elemMatch 연산자

  • $elemMatch 연산자는 Embedded Documents 배열을 쿼리할때 사용
# comments 중 "Charlie" 가 작성한 덧글이 있는 Document 조회를 했을때, 게시물 제목과 Charlie의 덧글부분만 읽고싶은 경우
# 이렇게 해보면 의도와는 다르게  Delta 의 덧글도 출력
> db.articles.find(
    {
        "comments": {
            $elemMatch: { "name": "Charlie" }
        }
    },
    {
        "title": true,
        "comments.name": true,
        "comments.message": true
    }
)
{
        "_id" : ObjectId("56c0ab6c639be5292edab0c6"),
        "title" : "article03",
        "comments" : [
                {
                        "name" : "Charlie",
                        "message" : "Hey Man!"
                },
                {
                        "name" : "Delta",
                        "message" : "Hey Man!"
                }
        ]
}

 

  • Embedded Document 배열이 아니라 아래 Document의 "name" 처럼 한개의 Embedded Document 경우
> db.users.find({
    "username": "velopert",
    "name": { "first": "M.J.", "last": "K."},
    "language": ["korean", "english", "chinese"]
  }
)
> db.users.find({ "name.first": "M.J."})
  • Document의 배열이아니라 그냥 배열일 시에는 다음과 같이 Query
> db.users.find({ "language": "korean"})

projection

  • find() 메소드의 두번째 parameter 인 projection
  • 쿼리의 결과값에서 보여질 field를 정할 대 사용
# article의 title과 content 만 조회

> db.articles.find( { } , { "_id": false, "title": true, "content": true } )
{ "title" : "article01", "content" : "content01" }
{ "title" : "article02", "content" : "content02" }
{ "title" : "article03", "content" : "content03" }

$slice 연산자

  • projector 연산자 중 $slice 연산자는 Embedded Document 배열을 읽을때 limit 설정
# title 값이 article03 인 Document 에서 덧글은 하나만 보이게 출력
$slice 가 없었더라면, 2개를 읽어와야하지만 1개로 제한을 두었기에 한개만 출력

> db.articles.find({"title": "article03"}, {comments: {$slice: 1}}).pretty()
{
        "_id" : ObjectId("56c0ab6c639be5292edab0c6"),
        "title" : "article03",
        "content" : "content03",
        "writer" : "Bravo",
        "likes" : 40,
        "comments" : [
                {
                        "name" : "Charlie",
                        "message" : "Hey Man!"
                }
        ]
}

 

 

$elemMatch

  • 위의 elemMatch 는 조건 연산자에서 사용하는 것이며,
  • 해당 elemMatch 는 필드 내, 즉 projection 파라메터 입니다.
# $elemMatch 연산자를 projection 연산자로 사용하면 이를 구현 가능
# comments 중 "Charlie" 가 작성한 덧글이 있는 Document 중 제목, 그리고 Charlie의 덧글만 조회 (필드 선언 시 다시 한번 더 eleMatch를 진행하게 되면 Delta 의 댓글은 안보임
> db.articles.find(
...     {
...         "comments": {
...             $elemMatch: { "name": "Charlie" }
...         }
...     },
...     {
...         "title": true,
...         "comments": {
...             $elemMatch: { "name": "Charlie" }
...         },
...         "comments.name": true,
...         "comments.message": true
...     }
... )
{ "_id" : ObjectId("56c0ab6c639be5292edab0c6"), "title" : "article03", "comments" : [ { "name" : "Charlie", "message" : "Hey Man!" } ] }

 

[findAndModify]

  • Single Document 에 대한 수정하고 반환
  • 반환 된 Document는 수정에 대한 결과가 아님
  • 반환 된 Document 에 수정한 내역을 확인하고 싶으면 new Option을 이용
    • new 옵션을 true로 하는 경우 변경 후의 결과 값을 보여주며, Default 로 new 옵션을(false) 선언하지 않는 경우 변경 되기 전 값을 출력
# mosters 에서 name이 'Demon' 의 att를 350으로 변경하고 변경한 후 결과 확인
> db.monsters.findAndModify({ query: { name: 'Demon' }, update: { $set: { att: 350 } }, new: true })

db.collection.findAndModify({
    query: <document>,
    sort: <document>,
    remove: <boolean>,
    update: <document or aggregation pipeline>, // Changed in MongoDB 4.2
    new: <boolean>,
    fields: <document>,
    upsert: <boolean>,
    bypassDocumentValidation: <boolean>,
    writeConcern: <document>,
    collation: <document>,
    arrayFilters: [ <filterdocument1>, ... ]
});

> db.monsters.findAndModify({
    query: { name: "Dragon" },
    update: { $inc: { "att": 1000 } ,$set :{"name":"Dragon","hp":4000,"att":1000}},
    upsert: true,
    new : true
})

> db.monsters.findAndModify({
    query: { name: "Dragon" },
    update: {$set :{"hp":2000,"att":2000}},
    upsert: true,
    new : true
})

> db.monsters.findAndModify({
    query: { name: "Dragon" },
    update: {$set :{"hp":3000,"att":2000}},
    upsert: true
})

# upsert / new
> db.monsters.find( {query:{ name: "Dragon_Baby" }} )
> db.monsters.findAndModify({
    query: { name: "Dragon_Baby" },
    update: {$set :{"hp":1000,"att":500}},
    upsert: true,
    new : true
})

# remove
> db.monsters.findAndModify({
    query: { name: "Dragon_Baby" },
    remove: true
})

# 여러개일 경우
> db.monsters.findAndModify({
    query: { name: "Dragon" },
    update: {$set :{"hp":4000,"att":5000}},
    upsert: true
})
  • new Option : true

  • upsert: true / new : true

  • remove : true
    • new:true
      • return 이 없기 때문에 에러
    • update : {xxxx}
      • 당연히 remove 되는데 update 에러 진행
    • remove 가 정상적으로 진행 시
      • 진행 이전 데이터 return  후 삭제

 

  • 여러 개일 경우
    • 역시 한 건만 변경 (update 처럼)

참고 : https://velopert.com/479

 

 

Cursor

 

반응형

기본 데이터 처리

  • ObjectID

    • 다만 cluster index는 아니며, mongodb 에는 모든 Index 가 Non-cluster Index
    • 아래와 같은 방식으로 생성 되기 때문에 Client에서 생성하여 제공?
    • 앞에 4byte는 유닉스 시간
    • 다음 3byte는 기기의 id 값
    • 다음 2byte는 프로세스 id 값
    • 마지막 3byte는 랜덤 값부터 시작하는 카운터로 구성
    • Document Insert 진행 시 _id 를 명시적으로 생성하지 않으면, "_id" 필드가 ObjectID 타입으로 자동 생성
    • _id 는 서로 겹치지 않는 ObjectID 타입으로 값을 할당 PK라고 생각하면 가능
    • 동시에 생성되어도 서로 다른 값이 생성되어 유일 값(unique)
    • ObjectID 값은 "유닉스시간+기기id+프로세스id+카운터" 로 구성
    • ObjectId.getTimestamp() 하면 생성된 시점을 알아낼 수 있음. (ObjectID를 이용하면 시간 range 로 검색이 가능)
> ObjectId("60371e375adcd7d623c78b3d").getTimestamp()
ISODate("2021-02-25T03:49:11Z")

> ObjectId("60371e8d5adcd7d623c78b3e").toString()
ObjectId("60371e8d5adcd7d623c78b3e")

> ObjectId("60371e8d5adcd7d623c78b3e").valueOf()
60371e8d5adcd7d623c78b3e

  • UUID

    • 출처 : charsyam-[입 개발] Global Unique Object ID 생성 방법에 대한 정리
    • Universally unique identifier 의 약어로, 16-octet(128bit) 크기의 32개의 Hexa로 표시
    • OSF에서 표준화(개방 소프트웨어 재단(Open Software Foundation)-유닉스 운영 체제의 일부로 오픈 표준을 만들 목적으로 1984년의 미국 National Cooperative Research and Production Act 하에 1988년에 설립된 비영리 단체)
    • UUID를 구현하는데는 다양한 방식이 있는데, MAC address 나 HASH(md5, sha-1) 등을 이용한 방식이 유명
    • MAC address 자체가 unique 하기 때문에, 여기에 현재의 시간을 붙이는 방식으로 구현이 가능

 

 

  • runCommand

    • Tool 에서 많이 보았던 command
    • 지정한 DB에서 도우미 제공해 주는 명령어
    • runCommand 를 사용하면, 내부 형태만 잡아 준다면 쉽게 접근이 가능
# 일반적으로 사용하는 명령어는 command 에 작성하여 사용 가능
# Default
> db.runCommand(
  {
	  명령어
  }
)

# 관리 administrative 명령어의 경우 아래와 같이 사용 가능
> db.adminCommand( { <command> } )

 

      • insert

> db.runCommand(
  {
    insert: <collection>,
    documents: [ <document>, <document>, <document>, ... ], // insert 할 내역을 array 형태로 작성
    ordered: <boolean>, // default : true (batch Insert 중 하나라도 실패하면 모든 명령어 실패 / false : Batch Insert 중 하나가 실패하더라도 다음 insert 진행)
    writeConcern: { <write concern> }, // write concern 으로 Transaction 처리하는 경우 명시적으로 설정 하지 말것. - Default 권장
    bypassDocumentValidation: <boolean>, // 유효성 검사로 Enable 하는 경우 유효성 검사를 생략
    comment: // Comment 작성으로 작성하게 되면 mongod log message 에 attr.command.cursor.comment 필드에 작성. (v4.4) -> 찾지 못했습니다. db.adminCommand( { getLog: } ) 참고 : https://docs.mongodb.com/manual/reference/command/getLog/#dbcmd.getLog 참고2 : https://docs.mongodb.com/manual/reference/log-messages/#log-messages-ref
  }
)

# insert Sample
> db.runCommand(
	{
      insert : 'employee'
      , documents:
      [
          {name : 'Hyungi.Kim'
          , Pos : ['dba','devops','develope']
          , wanted:['job','money']
          }
      ]
	}
)

# comment
> db.runCommand(
	{
		insert : 'employee'
		, documents:
		[
          {name : 'Hyungi'
          , Pos : ['dba','devops','develope']
          , wanted:['job','money']
          , memo : 'Add Comment'
          }
		]
	, comment : 'Comment Write by Hyungi.Kim'
	}
)

# inserMany
> db.runCommand(
  {
    insert : 'employee'
    , documents:
    [
      {name : 'Louis'
      , Pos : ['DB Engineer']
      , wanted:['dba']
      } ,
      {name : 'Bong'
      , Pos : ['Student']
      , wanted:['Soccer Player']
      }
    ]
  }
)

> db.runCommand(
  {
  insert : 'students'
  , documents:
    [
      { "_id" : 7, semester: 3, "grades" : [ { grade: 80, mean: 75, std: 8 },
        { grade: 85, mean: 90, std: 5 },
        { grade: 90, mean: 85, std: 3 } ] }
        ,
        { "_id" : 8, semester: 3, "grades" : [ { grade: 92, mean: 88, std: 8 },
        { grade: 78, mean: 90, std: 5 },
      { grade: 88, mean: 85, std: 3 } ] }
    ]
  }
)

 

      • update

# update 
> db.runCommand( 
   { 
      update: <collection>, 
      updates: [ 
         { 
           q: <query>,   // q : 검색 해야 하는 내역 name이 Louis 인 것 (where 절)
           u: <document or pipeline>,   // u : 변경해야 하는 내역 (set 절)
           upsert: <boolean>,  // default : false (true로 변경 시 존재 하지 않으면 Insert 진행)
           multi: <boolean>,  // default : false (true로 변경 시 존재하는 모든 데이터에 대해 update 진행)
           collation: <document>, 
           arrayFilters: <array>, 
           hint: <document|string> 
         }, 
         ... 
      ], 
      ordered: <boolean>, 
      writeConcern: { <write concern> }, 
      bypassDocumentValidation: <boolean>, 
      comment: <any> 
   } 
)
> db.runCommand(  
    { 
        update : 'employee' 
        , updates : [ 
            { 
                q: {name : 'Louis'} 
                ,u : [{$set: {Post : ['DBA','Data Engineer'], wated:['Data Engineer']}}] 
            } 
        ] 
    } 
)

> db.runCommand(  
    { 
        update : 'employee' 
        , updates : [ 
            { 
                q: {name : 'DotDot'}  
                ,u : [{$set: {Post : ['DBA','Data Engineer'], wated:['Wanted Girl Friend']}}] 
            } 
        ] 
    } 
)

> db.runCommand(  
    { 
        update : 'employee' 
        , updates : [ 
            { 
                q: {name : 'DotDot'} 
                ,u : [{$set: {Post : ['DBA','Data Engineer'], wated:['Wanted Girl Friend']}}]
                ,upsert: true
            } 
        ] 
    } 
)

> db.runCommand({find:'employee', filter : {Post:'Data Engineer'}})

# multi 를 true로 변경 시 match 되는 모든 데이터가 변경
# 단, 내부에 여러건이 있을 경우 덮어 쓰기 때문에 이 점 유의
> db.runCommand(  
    { 
        update : 'employee' 
        , updates : [ 
            { 
                q: {Post:'Data Engineer'}  
                ,u : [{$set: {Post : ['Data Science']}}] 
                ,multi : true
            } 
        ] 
    } 
)
# multi 를 default (false)로 하는 경우 한 건만 변경
> db.runCommand(  
    { 
        update : 'employee' 
        , updates : [ 
            { 
                q: {Post:'Data Science'}  
                ,u : [{$set: {Post : ['Data Master']}}]  
            } 
        ] 
    } 
)
  • update

  • upsert

  • multi

 

      • find
        • Projection
          • 명시하고자 하는 field 를 결정 가능
          • 연산자를 사용도 가능
            • $
              • fileter 에서 조건이 걸린 것 중, 원하는 필드 내 (배열로 구성) 첫 번째 값 리턴
              • ex) filter 로 a= 1인 것 중 b.$:1 로 설정하는 경우 b필드 배열 중 제일 첫번째 값 리턴
            • $eleMatch
              • Embedded Documents 배열을 쿼리할 때 사용
            • $slice
              • Embedded Documents 배열을 쿼리의 결과에 대한 원하는 리턴 개수
            • $meta
> db.runCommand( 
   { 
      "find": <string>,  // find : collection 명
      "filter": <document>,  // 검색 하고자 하는 내용 (where) - 작성하지 않으면 해당 collection 모두 반환
      "sort": <document>,    // order by 
      "projection": <document>,   // 명시 하고자 하는 컬럼명 (필드 결정) 연산자를 사용 가능 
      "hint": <document or string>, 
      "skip": <int>,  // default : 0 - 건너뛴 Document 개수 이후의 모든 값 리턴
      "limit": <int>,  // default : no-limit / 처음부터 원하는 limit 개수
      "batchSize": <int>, // 배치에서 반환할 문서 수. Default : 101 개
      "singleBatch": <bool>, 
      "comment": <any>, 
      "maxTimeMS": <int>, 
      "readConcern": <document>, 
      "max": <document>, 
      "min": <document>, 
      "returnKey": <bool>, 
      "showRecordId": <bool>, 
      "tailable": <bool>, 
      "oplogReplay": <bool>, 
      "noCursorTimeout": <bool>, 
      "awaitData": <bool>, 
      "allowPartialResults": <bool>, 
      "collation": <document>, 
      "allowDiskUse" : <bool> 
   } 
)

# find 
> db.runCommand(  
    {  
        find : 'employee' 
        , filter : {name : 'Louis'} 
        , projection : {name : 1} 
    }  
) 
> db.runCommand(   
    {  
        find : 'employee'  
        , projection : {name : 1}  
    }  
) 
> db.runCommand(   
    {  
        find : 'employee'  
        , filter : {name : 'Louis'}  
        , projection : {name : 1}  
        , sort : {name : 1} 
        , limit : 1 
    }  
)
$ 테스트
> db.runCommand( 
     { 
         insert : 'employee' 
         , documents: 
             [ 
    { "_id" : 7, semester: 3, "grades" : [ { grade: 80, mean: 75, std: 8 }, 
                                           { grade: 85, mean: 90, std: 5 }, 
...                                        { grade: 90, mean: 85, std: 3 } ] } 
... , 
... { "_id" : 8, semester: 3, "grades" : [ { grade: 92, mean: 88, std: 8 }, 
...                                        { grade: 78, mean: 90, std: 5 }, 
...                                        { grade: 88, mean: 85, std: 3 } ] } 
...             ] 
...     } 
... )
-> grades.mean 이 > 70 중, 만족하는 grades 의 첫번째 배열 값 리턴
> db.students.find(  
   { "grades.mean": { $gt: 70 } }, 
   { "grades.$": 1 }  
)

결과
{ "_id" : 7, "grades" : [ { "grade" : 80, "mean" : 75, "std" : 8 } ] } 
{ "_id" : 8, "grades" : [ { "grade" : 92, "mean" : 88, "std" : 8 } ] }

  • {배열:{$elemMatch:{원하는필드: 값}}}
    • 원하는 정보가 있으면 배열 내 모든 값이 리턴

  • {배열:{$slice:원하는 리턴 개수}}
    • ex) 댓글의 최대 개수 리턴

      • delete
        • capped collections 에서는 동작하지 않음.
        • 그 외는 동일한 동작 방식
> db.runCommand(
    { 
       delete: <collection>,  // 삭제할 collaction 명
       deletes: [ 
          { 
            q : <query>,  // 조건
            limit : <integer>,  // 삭제 개수 필수 (0: 조건에 맞는 모든 데이터 삭제, 1~n : 조건에 맞는 n개 삭제)
            collation: <document>, 
            hint: <document|string>, 
            comment: <any> 
          }, 
          ... 
       ], 
       ordered: <boolean>, 
       writeConcern: { <write concern> } 
    }
)

# limit 을 미 작성 시 오류 발생
> db.runCommand( 
    {  
       delete: "students"
        , deletes : [
            {q: {"_id" : 7} , limit:1}
        ]
    }
)

#대응 하는 모든 데이터 삭제는 limit : 0 으로 설정
> db.runCommand( 
    {  
       delete: "students" 
        , deletes : [ 
            {q: {"semester" : 5} , limit:0} 
        ] 
    } 
)

# And 조건

> db.runCommand( 
    {  
       delete: "students" 
        , deletes : [ 
            {q: {"semester" : 5, "_id":1} , limit:0}  
        ] 
    } 
)

Command Type

Name

Description

Aggregation

aggregate

Performs aggregation tasks such as group using the aggregation framework.

count

Counts the number of documents in a collection or a view.

distinct

Displays the distinct values found for a specified key in a collection or a view.

mapReduce

Performs map-reduce aggregation for large data sets.

Geospatial 

geoSearch

Performs a geospatial query that uses MongoDB’s haystack index functionality.

Command

delete

Deletes one or more documents.

find

Selects documents in a collection or a view.

findAndModify

Returns and modifies a single document.

getLastError

Returns the success status of the last operation.

getMore

Returns batches of documents currently pointed to by the cursor.

insert

Inserts one or more documents.

resetError

Deprecated. Resets the last error status.

update

Updates one or more documents.

Query Plan Cache

planCacheClear

Removes cached query plan(s) for a collection.

planCacheClearFilters

Clears index filter(s) for a collection.

planCacheListFilters

Lists the index filters for a collection.

planCacheSetFilter

Sets an index filter for a collection.

Authentication

authenticate

Starts an authenticated session using a username and password.

getnonce

This is an internal command to generate a one-time password for authentication.

logout

Terminates the current authenticated session.

User Management

createUser

Creates a new user.

dropAllUsersFromDatabase

Deletes all users associated with a database.

dropUser

Removes a single user.

grantRolesToUser

Grants a role and its privileges to a user.

revokeRolesFromUser

Removes a role from a user.

updateUser

Updates a user’s data.

usersInfo

Returns information about the specified users.

Role Management

createRole

Creates a role and specifies its privileges.

dropRole

Deletes the user-defined role.

dropAllRolesFromDatabase

Deletes all user-defined roles from a database.

grantPrivilegesToRole

Assigns privileges to a user-defined role.

grantRolesToRole

Specifies roles from which a user-defined role inherits privileges.

invalidateUserCache

Flushes the in-memory cache of user information, including credentials and roles.

revokePrivilegesFromRole

Removes the specified privileges from a user-defined role.

revokeRolesFromRole

Removes specified inherited roles from a user-defined role.

rolesInfo

Returns information for the specified role or roles.

updateRole

Updates a user-defined role.

Replication

applyOps

Internal command that applies oplog entries to the current data set.

isMaster

Displays information about this member’s role in the replica set, including whether it is the master.

replSetAbortPrimaryCatchUp

Forces the elected primary to abort sync (catch up) then complete the transition to primary.

replSetFreeze

Prevents the current member from seeking election as primary for a period of time.

replSetGetConfig

Returns the replica set’s configuration object.

replSetGetStatus

Returns a document that reports on the status of the replica set.

replSetInitiate

Initializes a new replica set.

replSetMaintenance

Enables or disables a maintenance mode, which puts a secondary node in a RECOVERING state.

replSetReconfig

Applies a new configuration to an existing replica set.

replSetResizeOplog

Dynamically resizes the oplog for a replica set member. Available for WiredTiger storage engine only.

replSetStepDown

Forces the current primary to step down and become a secondary, forcing an election.

replSetSyncFrom

Explicitly override the default logic for selecting a member to replicate from.

Sharding

addShard

Adds a shard to a sharded cluster.

addShardToZone

Associates a shard with a zone. Supports configuring zones in sharded clusters.

balancerCollectionStatus

Returns information on whether the chunks of a sharded collection are balanced.

New in version 4.4.

balancerStart

Starts a balancer thread.

balancerStatus

Returns information on the balancer status.

balancerStop

Stops the balancer thread.

checkShardingIndex

Internal command that validates index on shard key.

clearJumboFlag

Clears the jumbo flag for a chunk.

cleanupOrphaned

Removes orphaned data with shard key values outside of the ranges of the chunks owned by a shard.

enableSharding

Enables sharding on a specific database.

flushRouterConfig

Forces a mongod/mongos instance to update its cached routing metadata.

getShardMap

Internal command that reports on the state of a sharded cluster.

getShardVersion

Internal command that returns the config server version.

isdbgrid

Verifies that a process is a mongos.

listShards

Returns a list of configured shards.

medianKey

Deprecated internal command. See splitVector.

moveChunk

Internal command that migrates chunks between shards.

movePrimary

Reassigns the primary shard when removing a shard from a sharded cluster.

mergeChunks

Provides the ability to combine chunks on a single shard.

refineCollectionShardKey

Refines a collection’s shard key by adding a suffix to the existing key.

New in version 4.4.

removeShard

Starts the process of removing a shard from a sharded cluster.

removeShardFromZone

Removes the association between a shard and a zone. Supports configuring zones in sharded clusters.

setShardVersion

Internal command to sets the config server version.

shardCollection

Enables the sharding functionality for a collection, allowing the collection to be sharded.

shardingState

Reports whether the mongod is a member of a sharded cluster.

split

Creates a new chunk.

splitChunk

Internal command to split chunk. Instead use the methods sh.splitFind() and sh.splitAt().

splitVector

Internal command that determines split points.

unsetSharding

Deprecated. Internal command that affects connections between instances in a MongoDB deployment.

updateZoneKeyRange

Adds or removes the association between a range of sharded data and a zone. Supports configuring zones in sharded clusters.

Session

abortTransaction

Abort transaction.

New in version 4.0.

commitTransaction

Commit transaction.

New in version 4.0.

endSessions

Expire sessions before the sessions’ timeout period.

New in version 3.6.

killAllSessions

Kill all sessions.

New in version 3.6.

killAllSessionsByPattern

Kill all sessions that match the specified pattern

New in version 3.6.

killSessions

Kill specified sessions.

New in version 3.6.

refreshSessions

Refresh idle sessions.

New in version 3.6.

startSession

Starts a new session.

New in version 3.6.

Administration   Commands

cloneCollectionAsCapped

Copies a non-capped collection as a new capped collection.

collMod

Add options to a collection or modify a view definition.

compact

Defragments a collection and rebuilds the indexes.

connPoolSync

Internal command to flush connection pool.

convertToCapped

Converts a non-capped collection to a capped collection.

create

Creates a collection or a view.

createIndexes

Builds one or more indexes for a collection.

currentOp

Returns a document that contains information on in-progress operations for the database instance.

drop

Removes the specified collection from the database.

dropDatabase

Removes the current database.

dropConnections

Drops outgoing connections to the specified list of hosts.

dropIndexes

Removes indexes from a collection.

filemd5

Returns the md5 hash for files stored using GridFS.

fsync

Flushes pending writes to the storage layer and locks the database to allow backups.

fsyncUnlock

Unlocks one fsync lock.

getDefaultRWConcern

Retrieves the global default read and write concern options for the deployment.

 

New in version 4.4.

getParameter

Retrieves configuration options.

killCursors

Kills the specified cursors for a collection.

killOp

Terminates an operation as specified by the operation ID.

listCollections

Returns a list of collections in the current database.

listDatabases

Returns a document that lists all databases and returns basic database statistics.

listIndexes

Lists all indexes for a collection.

logRotate

Rotates the MongoDB logs to prevent a single file from taking too much space.

reIndex

Rebuilds all indexes on a collection.

renameCollection

Changes the name of an existing collection.

setFeatureCompatibilityVersion

Enables or disables features that persist data that are backwards-incompatible.

setIndexCommitQuorum

Changes the minimum number of data-bearing members (i.e commit quorum), including the primary, that must vote to commit an in-progress index build before the primary marks those indexes as ready.

setParameter

Modifies configuration options.

setDefaultRWConcern

Sets the global default read and write concern options for the deployment.

 

New in version 4.4.

shutdown

Shuts down the mongod or mongos process.

 

Diagnostic Commands

availableQueryOptions

Internal command that reports on the capabilities of the current MongoDB instance.

buildInfo

Displays statistics about the MongoDB build.

collStats

Reports storage utilization statics for a specified collection.

connPoolStats

Reports statistics on the outgoing connections from this MongoDB instance to other MongoDB instances in the deployment.

connectionStatus

Reports the authentication state for the current connection.

cursorInfo

Removed in MongoDB 3.2. Replaced with metrics.cursor.

dataSize

Returns the data size for a range of data. For internal use.

dbHash

Returns hash value a database and its collections.

dbStats

Reports storage utilization statistics for the specified database.

driverOIDTest

Internal command that converts an ObjectId to a string to support tests.

explain

Returns information on the execution of various operations.

features

Reports on features available in the current MongoDB instance.

getCmdLineOpts

Returns a document with the run-time arguments to the MongoDB instance and their parsed options.

getLog

Returns recent log messages.

hostInfo

Returns data that reflects the underlying host system.

isSelf

Internal command to support testing.

listCommands

Lists all database commands provided by the current mongod instance.

lockInfo

Internal command that returns information on locks that are currently being held or pending. Only available for mongod instances.

netstat

Internal command that reports on intra-deployment connectivity. Only available for mongos instances.

ping

Internal command that tests intra-deployment connectivity.

profile

Interface for the database profiler.

serverStatus

Returns a collection metrics on instance-wide resource utilization and status.

shardConnPoolStats

Deprecated in 4.4 Use :dbcommand:`connPoolStats` instead.

 

Reports statistics on a mongos’s connection pool for client operations against shards.

top

Returns raw usage statistics for each database in the mongod instance.

validate

Internal command that scans for a collection’s data and indexes for correctness.

whatsmyuri

Internal command that returns information on the current client.

 

Free Monitoring Commands

setFreeMonitoring

Enables/disables free monitoring during runtime.

 

Auditing   Commands

logApplicationMessage

Posts a custom message to the audit log.

 

    • non-CRUD 명령어도 사용 가능
      • 통계 정보 가져오기
      • 복제본 세트 초기화
      • 집계 파이프라인
      • map-reduce 작업 모두 가능
# mongodb 의 역활 확인 (mster 여부)
> db.runCommand( { isMaster: 1 } )
{ 
         "ismaster" : true, 
        "maxBsonObjectSize" :16777216, 
        "localTime" :ISODate("2013-01-06T19:53:43.647Z"), 
        "ok" : 1 
}

 

 

반응형
  • 알면 유용한 것에 대한 고민 끝에 Cursor 에 대해 알아 두면 좋지 않을까 하여 공유 드립니다.

Cursor

Name

Description

memo

cursor.addOption()

쿼리 동작을 수정하는 특별한 protocol flag 를 추가

 

cursor.allowDiskUse()

Sort 명령어를 사용하여, 쿼리 결과 정렬 작업을 처리하는 동안, 100 Mb 메모리를 초과하여 Disk Temporary files를 사용한 내역

--------------------------------------------------------------------------------

MongoDB의 Aggregate() 명령은 기본적으로 정렬을 위해서 100mb 메모리까지 사용 가능.

만약 그 이상의 데이터를 정렬해야 하는 경우라면 Aggregate() 명령은 실패-> 이 때 allowDiskUse 옵션을 true로 설정 시 Aggregate()처리가 디스크를 이용해 정렬 가능. 이 때 MongoDB 데이터 Directory 하의에 "_temp"  Diretory 를 만들어 임시 가공용 데이터 파일을 저장

Real MongoDB-702p 참고

 

$ db.user_scores.aggregate([

{$match:{score:{$gt:50}}},

{$group:{_id:"$name",avg:{$avg:"$score"}}}

],{allowDiskUse:True})

cursor.allowPartialResults()

find 명령어를 사용하여 샤딩된 collection의 작업을 진행 중 오류로 인해 조회를 못하게 되는 경우, 부분적인 결과만 이라도 조회

 

cursor.batchSize()

Single network 메시지에서, MongoDB에서 Client로 결과를 내보낸 document 수

 

cursor.close()

cursor close. (리소스까지 비움)

 

cursor.isClosed()

리턴이 성공하면 cursor를 close

 

cursor.collation()

find()에 의해 리턴된 커서의 collection 을 지정

 

cursor.comment()

system.profile Collection에서 로그 및 시스템에서 실행한 쿼리를 추적하기 위해 쿼리에 설명 추가(주석)

 

cursor.count()

커서에서 결과 Document의 count

 

cursor.explain()

커서에서 쿼리 실행결과 보고

 

cursor.forEach()

커서에서 모든 Document에 대한 JavaScript 함수를 적용

 

cursor.hasNext()

Cursor 내 반환할 Document가 존재하면 True 를 리턴

 

cursor.hint()

쿼리에 특정 인덱스를 사용

 

cursor.isExhausted()

커서가 닫혀 있고, 배치에 남아있는 Document가 없는 경우 true를 반환

 

cursor.itcount()

커서 내 클라이언트로 제공할 Document의 수를 계산 (find().count()와 유사하지만, cursor에서 사용하는 영역?)

 

cursor.limit()

cursor document 결과 를 제한

 

cursor.map()

커서 결과 Document를 함수에 적용하고, 그 결과 값을 배열(Array) 형태로 저장

db.users.find().map( function(u) { return u.name; } );

-> forEach와 유사

cursor.max()

find 필드 값에 대한 max 값을 지정. cursor.hint()와 함께 사용

 

cursor.maxTimeMS()

cursor 작업의 누적 시간 제한 (ms)

 

cursor.min()

find 필드 값에 대한 min 값을 지정. cursor.hint()와 함께 사용

 

cursor.next()

cursor 내에서 다음 Document 를 반환

 

cursor.noCursorTimeout()

cursor 자동 닫힘의 Timeout 을 비활성화

 

cursor.objsLeftInBatch()

현재 cursor 내 남아있는 document 수를 반환

 

cursor.pretty()

cursor 결과를 읽기 쉽게 표시

 

cursor.readConcern()

find() 명령어에 대한 readConcern 을 지정

 

cursor.readPref()

레플리카셋에서 클라이언트가 다이렉트로 읽을 수 있도록 cursor 설정

 

cursor.returnKey()

Document가 아닌 인덱스 key를 반환하도록 커서를 수정

 

cursor.showRecordId()

결과 Document 에 내부 엔진 ID 필드를 추가

 

cursor.size()

skip()와 limit() 를 적용한 커서내 결과 Document count 를 리턴

 

cursor.skip()

커서 내 Document에서 skip 또는 패스한 후의 결과를 리턴

 

cursor.sort()

정렬값에 의해 정렬된 결과를 리턴

 

cursor.tailable()

capped collection에서 커서에 tail 하여 제공 (마지막 내역만 계속 공유?)

 

cursor.toArray()

커서에 의해 반환된 Document를 배열로 반환

 

cursor.forEach()

  • 예전 검색해서 저장해 놓은 쿼리
db.getCollectionNames().forEach(function(collection) {
  indexes = db[collection].getIndexes();
  print("Indexes for " + collection + ":");
  printjson(indexes);
});

cursor.itcount()

> db.SentMessages.find({Type : 'Foo'})
{ "_id" : ObjectId("53ea19af9834184ad6d3675a"), "Name" : "123", "Type" : "Foo" }
{ "_id" : ObjectId("53ea19dd9834184ad6d3675c"), "Name" : "789", "Type" : "Foo" }
{ "_id" : ObjectId("53ea19d29834184ad6d3675b"), "Name" : "456", "Type" : "Foo" }

> db.SentMessages.find({Type : 'Foo'}).count()
3

> db.SentMessages.find({Type : 'Foo'}).limit(1)
{ "_id" : ObjectId("53ea19af9834184ad6d3675a"), "Name" : "123", "Type" : "Foo" }

> db.SentMessages.find({Type : 'Foo'}).limit(1).count();
3

> db.SentMessages.aggregate([ { $match : { Type : 'Foo'}} ])
{ "_id" : ObjectId("53ea19af9834184ad6d3675a"), "Name" : "123", "Type" : "Foo" }
{ "_id" : ObjectId("53ea19dd9834184ad6d3675c"), "Name" : "789", "Type" : "Foo" }
{ "_id" : ObjectId("53ea19d29834184ad6d3675b"), "Name" : "456", "Type" : "Foo" }

> db.SentMessages.aggregate([ { $match : { Type : 'Foo'}} ]).count()
2014-08-12T14:47:12.488+0100 TypeError: Object #<Object> has no method 'count'

> db.SentMessages.aggregate([ { $match : { Type : 'Foo'}} ]).itcount()
3

> db.SentMessages.aggregate([ { $match : { Type : 'Foo'}}, {$limit : 1} ])
{ "_id" : ObjectId("53ea19af9834184ad6d3675a"), "Name" : "123", "Type" : "Foo" }

> db.SentMessages.aggregate([ { $match : { Type : 'Foo'}}, {$limit : 1} ]).itcount()
1

> exit
bye

 

batchSize 와 Limit 비교

  • 참고(복붙이나 다름 없습니다.) : https://emflant.tistory.com/12
  • batchSize : 한 batch 당 가져오는 document 갯수.
  • limit : 쿼리의 결과로 가져올 총 갯수.
  • limit 와 batchSize 를 지정하지 않는 경우 batch는 한번 당 101개의 Document 결과를 리턴 하지만, Document 당 너무 많은 데이터가 있는 경우 batch 한번 당 1Mb 가 최대 size
  • limit 와 batchSize 를 지정하는 경우, 지정한 수만큼 리턴
    • 큰 수로 셋팅하더라도 4Mb 이상의 데이터를 한번의 Batch로 가져올 수 없음.
    • 인덱스 없이 데이터를 sort하는 경우 첫 번째  batch에 모든 데이터를 가져오지만, 최대 4 Mb 초과할 수 없음.
> // for문을 돌려서 간단한 데이터로 200개 document 를 등록한다.
> for (var i = 0; i < 200; i++) { db.foo.insert({i: i}); }
> var cursor = db.foo.find()
> // batchSize나 limit 값 지정없이 find 했으므로 기본 batch 크기인 101 documents
> cursor.objsLeftInBatch()
101

> // 한번에 모든 document들을 가져오기 위해 큰 limit 값을 셋팅하면 batchSize는 모든 document 수가 된다.
> var cursor = db.foo.find().limit(1000)
> cursor.objsLeftInBatch()
200

> // batchSize 가 limit 크기보다 작으면 batchSize가 우선한다.
> var cursor = db.foo.find().batchSize(10).limit(1000)
> cursor.objsLeftInBatch()
10

> // limit 가 batchSize 보다 작으면 limit 가 우선한다.
> var cursor = db.foo.find().batchSize(10).limit(5)
> cursor.objsLeftInBatch()
5

> // 각각 1MB 데이터로 10개의 document 를 등록한다.
> var megabyte = '';
> for (var i = 0; i < 1024 * 1024; i++) { megabyte += 'a'; }
> for (var i = 0; i < 10; i++) { db.bar.insert({s:megabyte}); }

> // limit나 batchSize를 지정하지 않았으므로 첫번째 batch는 1MB 에서 멈춘다.
> // 결국 1개씩 반복적으로 데이터를 가져오게됨
> var cursor = db.bar.find()

> cursor.objsLeftInBatch()
1

 

반응형

기본적인 명령어

  • runCommand
    • tool 에서 많이 보인 command
    • 지정한 DB에서 도우미를 제공해 주는 명령어
    • document 또는 String type
      • Database 명령어로 문자열 또는 Document 형태의 명령어로 반환 또한 Document, string으로 반환
    • 동작방식
      • db.runCommand()는 현재 선택된 DB 에서만 명령을 실행
      • 일부 명령은 admin DB에서만 적용되며, 이러한 명령을 실행하기 위해서는 admin 으로 변경하거나 adminCommand()를 사용
    • 명령 결과

Field

Description

ok

명령 성공 실패의 여부를 표시

operationTime

수행된 작업 시간

oplog 항목의 Timestamp로 MongoDB에 표시

Replica set 과 Shard cluster 에 해당

 

만약,명령어가  oplog 항목을 생성하지 않는 경우

ex) 읽기 작업의 경우, 해당 항목을 표시하지 않음

이때 운영 시간을 반환

 

Read concern을 "majority"와 "loinearizable" 인 경우, timestamp를 oplog의 가장 최근에 완료된 항목 값을 사용.

Consistent session과 관련된 작업의 경우, MongoDB Driver는 Read 작업과 클러스터 시간을 자동으로 설정하여 사용 (v3.6)

$clusterTime

적용된 cluster 시간을 반환

cluster time은 작업 순서에 사용되는 논리적인 시간

Cluster와 replica set일 경우에 해당하며, Mongodb 내부 동작에서만 사용

ex)

참고 : https://docs.mongodb.com/manual/reference/method/db.runCommand/

https://docs.mongodb.com/manual/reference/command/

https://docs.mongodb.com/manual/reference/command/insert/#dbcmd.insert

 

 

  • Database
    • Database 생성 방법
      • USE 구문을 사용해서 생성 가능
      • USE 구문을 사용하면 DB선택도 가능
      • 하지만, 단순 빈 Database 는 생성 하더라도 다시 확인 시 보이지 않음( mongo> show dbs )
        • 하나의 Document 를  추가해 줘야 database가 생성 된 것을 확인 가능
    • Collection (Table) 생성 방법
      • 생성하는 방법은 2가지가 존재 하지만, 특별한 상황이 아니라면 Document를 추가(insert)하여 collection이 생성 되도록 진행
      • createCollection을 이용한 명시적 생성은 Collection의 세부 정보를 수정할 수 있기에, 잘 알고 사용하지 않을 경우 예상 치 못한 장애를 발생 시킬 수 있기에, 미연에 방지하고자 하기 위함
        • 4.2부터는 MMAPv1 Storage engine과 그에 대한 createCollection에서 사용 가능한 option도 삭제
      • Show collections 로 collection 리스트 확인 가능 ( mongo> show collections )
      • 지향 : db.컬레션명.insert()
        • Ex) people이라는 Collection 추가 및 name, age 필드 추가
        • db.people.insert({"name":"Louis.Kim"},{"age":36})
      • 지양 : db.createCollection("people")  또는 db.createCollection("people",{Option 및 Option 값})
    • CreatedCollection
      • capped Collection
        • db.createCollection(<컬렉션명>, {capped : true, size:4096})
        • 일반 collection 과 다르게, 정해진 크기를 초과하게 되면 자동으로 가장 오래된 데이터를 삭제
          • 반대로 유저가 임의로 데이터를 삭제는 불가능
          • 삭제를 하고자 한다면, drop 만 가능
        • 원형 버퍼와 유사한 방식으로 동작
        • Capped Collection의 대안으로 MongoDB의 TTL 인덱스 고려 가능
          • Collection 에 TTL를 설정하여 Expire 데이터를 제거 가능.
          • Capped Collection 는 TTL index 와 호환되지 않음
        • Sharding 지원하지 않음
        • Aggregation pipeline 단계의 $out 를 capped collection의 결과를  wirte 할 수 없음
        • size는 byte 단위
        • 최소 size는 4096 byte (Collection이 기본으로 차지하는 size 때문)
        • Document의 삽입 속도가 매우 빠름
          • order를 하지 않은 collection을 find하는 경우, Insert 한 순서대로 결과를 가져오기 때문에 순서 보장하기에 매우 빠름
          • 가장 최근에 삽입된 요소를 효율적으로 검색 가능
        • 크기를 지정하고 사용하므로 추가 공간 필요 없음
> db.createCollection(name, {capped: <Boolean>, autoIndexId: <Boolean>, size: <number>, max <number>} )

 

파라미터

 타입

 설명

 name

 string

생성할 도큐먼트의 이름

 options

 document

컬렉션의 옵션 부여

option

 필드

 타입

 설명

 capped 

 boolean

capped이면 "true"를 아니면 "false"를 할당

 autoIndexId

 boolean

capped이면 "true"를, 그 외에는 "false"를 할당

 size

 number

저장 공간의 크기 지정

 max

 number

저장할 도큐먼트의 최대 개수 지정

혼자만의 고민

capped Collection

> 만약 100일치의 데이터를 보관이 정책인데, 들어가는 데이터 size가 크면 100일치 보다 더 일찍 삭제 될꺼고, size가 작으면 100일 치 보다 더 많이 남아 있을 껀데....???

> 차라리 storage 공간이 한정적이라서 중요하지는 않지만 보관이 필요한 경우 한정된 자원속에서만 저장이 필요한 경우, 관리 포인트를 없애기 위해 사용...

> 하지만 데이터가 지속적으로 삭제 추가 되는 상황이라면 I/O도 꾸준히 있을듯....여러모로 사용을 안하는 것이..

> 로그 데이터나, 일정 시간 동안만 보관하는 통계 데이터를 보관하고 싶을 때 유용할 것 같음.

      • view
        • 미리 설정한 내용에 대해 읽을 수만 있는 뷰
        • 실제로 데이터를 저장해서 불러오지 않기 때문에 사용할 수 있는 명령어에 제약(집계 파이프라인의 문법을 이용)

 

  • Document

    • [Delete]
      • Delete(권장)
        • 3.2부터 Remove를 대체하는 메소드 추가
        • db.컬렉션명.deleteMany(조건)db.employee.deleteMany({"name":"Louis.Kim"})
        • Ex) employee 컬렉션에서 name이 Louis.Kim 을 삭제
        • db.컬렉션명.deleteOne(조건)
          • 해당 조건에 맞는 한건만 삭제

 

      • Remove(지양)
        • db.컬렉션명.remove(조건)
        • deleteMany 와 remove 차이는 거의 없지만, 앞으로 remove는 지원하지 않는다고 예정하고 있기에 deleteMany로 삭제 진행 권장
        • 추가로, 한 건만(유일값 이라고 단정 지을 수 있다면) 삭제 한다면 deleteOne이 deleteMany보다 성능이 미세하지만 더 낫기에 상황에 맞게 사용하면 됨(Single Transaction 처리냐, Multi Transaction  차이 여부)

 

    • [Update]
      • 권장
        • Update  진행 시 대소문자 구분하여 검색 및 업데이트 진행
        • 여러건의 Update가 아니라면, 무분별하게 Multi 옵션을 사용하지 않는 것을 추천.
        • explain을 확인하여 Index 사용 여부를 확인하며, _id를 이용한 검색을 활용
        • save 명령어는 지양 (insert / update 모두 해당)
        • Update이 후 존재하지 않으면 Insert하는 경우에만 upsert Option을 이용하며, 그게 아닌 곳에서는 Insert / Update를 명시해서 사용
db.collection.update(

  {찾을 조건},

  {$set:{변경할 필드 내용}}

  , {

    upsert: <boolean>, // 업데이트할 내용이 없다면 새로운 Document추가, Default는 False로 없으면 업데이트 안함

    multi: <boolean>, // 여러건 업데이트 여부, Default는 False로 한건만 업데이트

    writeConcern: <document>,

    Collation: <document>, // 3.4

    arrayFilters: [ <filterdocument1>, ... ], //3.6

    hint : <document | string>. // Mongodb 4.2

  } // Option 생략 가능

)

 

      • db.collection명.update({조건}, {$set:{변경하고자 필드내용}})
        • Monster Collection에서 Slime의 hp를 30으로 변경// WriteResult({ nMatched: 1, nUpserted: 0, nModified: 1 });
        • db.monsters.update({ name: 'Slime' }, { $set: { hp: 30 } });
      • 만약 $set을 추가 하지 않고 진행 시 전부 지워지고 {변경하고자 필드 내용}만 남음
        • db.monsters.update({ name: 'Slime' }, { hp: 30 } );
        • 이렇게 하면 결과는 hp:30만 남고 모든 내용 삭제 됨
      • 추가 $inc 를 사용하여 기존 데이터를 손쉽게 제어 가능 
        • Slime의 hp를 현재 얼마인지 몰라도 -30 해보자// WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
        • - db.monsters.update({name:'Slime'},{$inc:{hp:-30}})
      • Update를 해야하는 것이 한건이 아니라 여러건인 경우 multi Option을 추가해 줘야함
        • (Multi Option 을 추가 하지 않는 경우 한건만 변경)// WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })
        • > db.monsters.update({name:'Slime'},{$inc:{hp:-80}},{multi:true})

 

[Update - Option]

Parameter

Type

설명

upsert

boolean

Optional. (기본값: false) 이 값이 true 로 설정되면 query한 document가 없을 경우, 새로운 document를 추가

multi

boolean

Optional. (기본값: false)  이 값이 true 로 설정되면, 여러개의 document 를 수정

writeConcern

document

Optional.  wtimeout 등 document 업데이트 할 때 필요한 설정값

기본 writeConcern을 사용하려면 이 파라미터를 생략

Write Concern — MongoDB Manual

Collation

document

데이터 정렬을 지정

: 데이터 정렬을 사용하면 소문자 및 악센트 표시 규칙과 같이 문자열 비교를 위한 언어별 규칙을 지정할 수 있음.

한국어는 필요한지 의문(대,소문자 악센트 등의 규칙이 없음)

다국어 지원시 고려해 봐야 될 부분

collation: {

   locale: <string>, // 해당국가 언어 특성 적용(

   caseLevel: <boolean>,

   caseFirst: <string>,

   strength: <int>,  // 1: 대소문자 구문 안함(default :0 대소문자구분)

   numericOrdering: <boolean>,

   alternate: <string>,

   maxVariable: <string>,

   backwards: <boolean>

}

 

arrayFilters

Array

Array Filter에서 업데이트 작업을 위해 수정해야할 배열 요소를 결정하는 설정

$[<조건>]를 사용하여 조건 지정

 

Hint

Document or string

Index  를 강제로 지정 가능

Ex) status index를 강제로 사용

 

db.members.createIndex( { status: 1 } )

db.members.createIndex( { points: 1 } )

 

db.members.update(

   { points: { $lte: 20 }, status: "P" },     // Query parameter

   { $set: { misc1: "Need to activate" } },   // Update document

   { multi: true, hint: { status: 1 } }       // Options

)

[Update]

[Multi - Option Test]

[Collation - 대소문자 비교]

  • 기본적으로 대소문자 구분

  • Collation의 strength 을 적용 후 진행

[arrayFilters]

  • Find
    • Find 관련 명령어에는 여러개가 있으며, 그 중에 많이 사용하는 위주로 가이드 하며, 필요 시 추가 가이드 진행 예정
    • Find명령어 사용 시 필요한 filed 명을 사용하여 검색을 추천
    • Ex) db.bios.find( {조건}, {_id:0, name:1 , money:1})
      • 만약 empno만 빼고 다 보고자 하면 그때는 > db.thing.find( { }, {empno: 0} ) 이런식으로 표시
      • _id에 대해서만 혼용 사용 가능하며, 다른 필드들에 대해서는 1과 0을 혼용해서 표시 안됨
    • 조건 검색 시 가급적  _id를 작성하며 Range 검색 시 기간을 지정하여 검색 추천

      Ex) db.bios.find( {"_id":{"$gte":ObjectId("5dfaf5c00000000000000000", "$lte":ObjectId("5dfaf5c00000000000000000")}, {_id:0, name:1 , money:1})
      (오늘 이전 모든 데이터 검색 or 오픈 이후 모든 날짜에 대해 검색 같은 전체 검색은 지양, 어제부터 일주일 전, 현재부터 하루 전 데이터 식으로 검색을 추천)
    • filed 명시 방법
> db.thing.find( { }, {empno: 1} )

// empno 를 표시하고, _id는 default로 표시, 단 그 외(ename) 은 표시 안함



> db.thing.find( { }, {empno: 0} )

// empno 만 표시를 안하고 나머지 ename , _id는 표시



> db.thing.find( { }, {_id:0, empno: 1} )

// empno만 표시하고, _id 및 다른 필드는 표시 안함.

// 여기서 중요한건 _id는 항상 명시해 줘야 하며, 보고 싶은 필드만 1로 설정해서 표시



// 다른 필드의 경우 ename이 있더라도 ename:0 으로 하면 에러 발생 >> 왜?????

> db.thing.find( { }, {empno: 1, ename:0} )

명령어

내역

find()

검색

findAndModify()

검색 후 수정

Update, upset, remove 모두 가능

new : true를 설정하여 update 이후 값을 리턴

new : false 또는 미적용 시 update 이전 값을 리턴

 

db.monsters.findAndModify({

    query: { name: "Dragon" },

    update: { $inc: { att: 1000 } ,$set :{"name":"Dragon","hp":4000,"att":1000}},

    upsert: true,

    new : true

})

findOne()

한건만 검색

findOneAndDelete()

한건만 검색 후 삭제

findOneAndReplace() > v3.2

한건만 검색 후 변경

returnNewDocument : true 설정하여 변경 전후 확인 가능

Replace 와 Update의 경우 Update는 명시한 필드만 변경 되지만, Replace의 경우는 명시한 필드 변경 외에는 나머지 필드는 모두 삭제 됨

가급적이면 Update만 사용 해야함

findOneAndUpdate()  > v3.2

한건만 검색 후 변경

returnNewDocument : true 설정하여 변경 전후 확인 가능

[findAndModify]

db.monsters.findAndModify({ query: { name: 'Demon' }, update: { $set: { att: 350 } }, new: true })

[findAndModify]

db.monsters.findAndModify({

query: { name: "Dragon" },

update: { $inc: { att: 1000 } ,$set :{"name":"Dragon","hp":4000,"att":1000}},

upsert: true,

new : true

})

 

  • Cursor
    • 쿼리 결과에 대한 포인터
    • find 명령어는 결과로 Document를 반환하지 않고 Cursor를 반환(Pointer)
      • 성능을 높이기 위함(결과 값을 리턴하는 것이 아닌 값의 결과들을 모아 놓은 주소를 반환한다고 이해)
    • 커서는 일시적으로 결과를 읽어내려고 존재하기 때문에 10분의 제한시간 이후에는 비활성 상태 전환
    • find 명령어를 실행하면 batch 라는 곳에 검색한 결과를 저장
    • 일반적으로 101개의 document 를 batch에 모아 놓고 20개씩 커서가 가르킴
    • 한 개의 document를 불러오기 위해서는 next()로 호출
    • 커서로 batch 에 102번째 document를 불러오려고 하면 batch에 쿼리 결과를 102번째부터 시작해서 총 101개를 담고, 102번째 Document를 cursor 가 가르킴
    • 커서를 이용한 Document 반환
      • find 명령어 모든 document를 모두 불러오기 위해서는 toArray() 메소드를 이용하면 모든 정보를 가져올 수 있음
> var cursor = db.cappedCollection.find()

> cursor

> db.cappedCollection.find()

결과...

> cursor.next()

하나만 리턴?

> cursor.hasNext()

true
    • 모든 document 가져오는 방법
> db.cappedCollection.find().toArray()

 

    • toArray() 메소드 특징은 find문의 모든 결과를 담은 배열을 반환
      • 모든 값이 다 필요하지 않다면 toArray 메소드는 비효율적
      • Document 총 크기가 매우 크다면 toArray 메소드를 사용할 시 사용 가능한 메모리 용량을 초과해 버릴 수 있음
      • forEach 메소드를 이용하여 커서로 각각의 Document를 순차적으로 불러와서 작업 가능 (메모리 효율적인 사용 가능)
    • https://docs.mongodb.com/manual/reference/method/js-cursor/

 

[그 외]

 

  • Terminate Running Operations
    • maxTimeMS()와 db.killOp() 으로 실행 중인 작업을 종료(kill) 가능
    • MongoDB 배치에서 작업의 동작을 제어하기 위해 필요에 따라 해당 작업을 사용

 

  • maxTime MS
    • 작업 시간 제한을 설정
    • 작업이 지정된 시간 제한인 maxTimeMS() 에 도달하면, MongoDB가 다음 interrupt 지점에서 작업을 중단
    • 샘플
      • 중단 쿼리 설정
        • mongo shell에서 , 쿼리의 시간을 30 ms 으로 설정
- location collection 에서 town이라는 필드에 대해 maxTimeMS를 30 ms 설정

db.location.find( { "town": { "$regex": "(Pine Lumber)", "$options": 'i' } } ).maxTimeMS(30)

 

      • 중단될 명령어 실행
        • 잠재적으로 오래 실행될 것이라는 명령어 실행
        • city key가 존재하는 각각의 개별 collection 필드를  반환하는 명령어 실행
db.runCommand( { distinct: "collection", key: "city" } )

 

        • maxTimeMS 를 45ms으로 필드에 대해 추가도 가능
db.runCommand( { distinct: "collection",key: "city",maxTimeMS: 45 } )

 

  • KillOp
    • db.killOp() 는 작업 ID로 실행 중인 작업을 kill
    • 명령어 : db.killOp(<opId>)
    • 해당 명령어는 클라이언트를 종료할 뿐이지, DB 내부에서는 해당 명령어는 작업 종료 안함?

 

  • Sharded Cluster
    • MongoDB4.0에서 부터는, mongos 에서 KillOp 명령을 사용하여 Cluster 내 shard에 걸쳐서 실행되고 있는 쿼리를 kill 할 수 있음(read 작업).
    • Write 작업에 대해서는, mongos에서 killOp 명령어로 각 샤드에 존재하는 쿼리를 kill 할 수 없음.
    • Shard 에 대해서는 아래 참고
    • 샤드 작업 리스트 확인 : Mongos에서 $currentOp의 localOps를 참고

 

 

[참고]

https://www.zerocho.com/category/MongoDB/post/579e2821c097d015000404dc

https://velopert.com/545

https://docs.mongodb.com/manual/reference/method/db.collection.update/

https://docs.mongodb.com/manual/reference/collation-locales-defaults/

https://velopert.com/479

https://cinema4dr12.tistory.com/373  (capped collection)

https://velopert.com/479 find 관련한 상세한 설명

 

반응형

Sharding의 정의

  • 같은 테이블 스키마를 가진 데이터를 다수의 데이터베이스에 분산하여 저장하는 방법
  • Application Level 에서도 가능 (RDBMS 에서 Sharding) 하지만, Databas Level에서도 가능 (ex-MongoDB / Redis 등)
  • Horizontal Partitioning (수평파티션) 이라고도 함

 

Sharding  적용

  • 프로그래밍, 운영적인 복잡도는 증가하고 높아지는 것을 뜻함
  • 가능하면 Sharding을 피하거나 지연시킬 수 있는 방법
    • Sacle Up
      • 하드스펙을 올리는 것
    • Read 부하가 크면
      • Replication 을 두어 Read 분산 처리
    • Table의 특정 컬럼만 사용 빈도수가 높다면
      • Vertically Partition(수직 파티션)을 진행
      • Data를 Hot, Warm, Cold data로 분리하여 처리
        • Memory DB를 활용
        • 테이블의 데이터 건수를 줄이는 것

Sharding 방식

  • Range Sharding (range based sharding)
    • key (shard key) 값에 따라서 range 를 나눠서 데이터 를 분배하는 방식
    • 비교적 간단하게 sharding이 가능
    • 증설에 재정렬 비용이 들지 않음
    • Shard key 의 선택이 중요
      • Shard key에 따라 일부 데이터가 몰릴 수 있음(hotspots)
      • 트래픽이 저조한 DB가 존재
  • Hash Sharding (hash based sharding)
    • key를 받아 해당 데이터를 hash 함수 결과로 분배
    • Hotspot를 방지하고 균등하게 분배
    • 재분배를 해야하는 경우(삭제 또는 추가) 전체 데이터를 다시 hash value를 이용하여 분배 (Migration 에 어려움)
  • Directory Based Sharding
    • shard내 어떤 데이터가 존재하는 지 추적할 수 있는 lookup table이 존재
    • range based sharding 과 비슷하지만, 특별한 기준에 의해 shard를 나눈 것이라, 동적으로 shard를 구성 가능
      • range나 hash 모두 정적인 반면, 해당 sharding은 유연성 있게 임의로 나누는 것이라 유연성을 갖춤
    • 쿼리 할 때 모두 lookup table 를 참조하기 때문에 lookup table이 문제를 일으킬 소지를 보유
      • lookup table이 hot spot 가능성
      • lookup table이 손상되면 문제 발생

 

MongoDB  Sharding

 

  • 분산 처리를 통한 효율성 향상이 가장 큰 목적이므로 3대 이상의 샤드 서버로 구축을 권장(최소 2대)
  • 싱글 노드 운영보다 최소 20~30% 추가 메모리 요구 (MongoS와 OpLog, Balancer 프로세스가 사용하게 될 추가 메모리 고려)
  • 샤드 시스템에 구축되는 config 서버는 최소 3대 이상 활성화를 권장.
    • Config 서버는 샤드 시스템 구축과 관련된 메타 데이터를 저장 관리하며 빠른 검색을 위한 인덱스 정보를 저장, 관리하고 있기 때문
    • 샤드 서버와는 별도의 서버에 구축이 원칙
    • Config 서버는 샤드 서버보다 저사양의 시스템으로 구축 가능

Config Server

 

  • Config 서버는 샤딩 시스템의 필수 구조
  • 최소 1대가 요구되며 장애로 인해 서비스가 중지되는 것을 피하기 위해 추가로 Config 서버 설정이 필요.(HA 구성 필요-PSS-필수)
  • Config 서버는 각 샤드 서버에 데이터들이 어떻게 분산 저장되어 있는지에 대한 Meta Data가 저장 (Shard 정보를 저장 관리)
    • Shard Meta 정보
      • MongoS가 처리하는 Chunk 단위로 된 chunk 리스트와 chunk들을의 range 정보를 보유
    • 분산 Lock
      • MongoS들 간의 config 서버와의 데이터 통신 동기화를 위해 도입
      • 샤딩을 수행할 연산들에 대해 분산 락을 사용
        • 여러개의 mongos가 동시에 동일한 chunk에 대한 작업을 시도하는 등의 이슈를 방지하기 위함
        • 작업을 수행하기 전 config server의 locks collection 의  lock을 획득 후에만 작업 가능
repl_conf:PRIMARY> db.locks.find() 
{ "_id" : "config", "state" : 0, "process" : "ConfigServer", "ts" : ObjectId("5d6b7f15a9f5ecd49052a36f"), "when" : ISODate("2019-09-01T08:19:33.165Z"), "who" : "ConfigServer:conn164", "why" : "createCollection" } 
{ "_id" : "config.system.sessions", "state" : 0, "process" : "ConfigServer", "ts" : ObjectId("5d6b7f15a9f5ecd49052a376"), "when" : ISODate("2019-09-01T08:19:33.172Z"), "who" : "ConfigServer:conn164", "why" : "createCollection" } 
{ "_id" : "testdb", "state" : 0, "process" : "ConfigServer", "ts" : ObjectId("5d62889d3ed72a6b6729a5ca"), "when" : ISODate("2019-08-25T13:09:49.728Z"), "who" : "ConfigServer:conn24", "why" : "shardCollection" } 
{ "_id" : "testdb.testCollection2", "state" : 0, "process" : "ConfigServer", "ts" : ObjectId("5d62889d3ed72a6b6729a5d1"), "when" : ISODate("2019-08-25T13:09:49.738Z"), "who" : "ConfigServer:conn24", "why" : "shardCollection" } 
{ "_id" : "test.testCollection2", "state" : 0, "process" : "ConfigServer", "ts" : ObjectId("5d6288ab3ed72a6b6729a65a"), "when" : ISODate("2019-08-25T13:10:03.834Z"), "who" : "ConfigServer:conn24", "why" : "shardCollection" }

 

      • Lock 역할
        • 밸런서(balancer)의 연산
        • Collection 의 분할(split)
        • Collection 이관(migration)

          • LockPinger : 해당 쓰레드는 30초 주기로 config 서버와 통신
    • 복제 집합 정보 : MongoS가 관리, 접속해야 하는 Mongo Shard 정보
  • MongoS가 데이터를 쓰고/읽기 작업을 수행할 때 Config 서버는 MongoS를 통해 데이터를 동기화-수집 진행

 

MongoS

 

  • 데이터를 Shard 서버로 분배해 주는 프로세스 (Router-Balancer)
    • Data를 분산하다 보면 작업의 일관성을 위하여 Lock을 사용
    • 이때 Chunk Size를 적절하게 설계하지 못하면 Migration 때문에 성능 저하 현상이 발생 가능성
    • DB 사용량이 적은 시간대 Balancer를 동작시키고 그 외 시간에는 끄는 방법도 성능 향상의 방법
  • 하나 이상의 프로세스가 활성화  가능(여러대의 MongoS를 운영 가능)
  • Application Server에서 실행 가능 (Application에서 직접적으로 접속하는 주체이며,독립적인 서버로 동작 가능하며,  Application 서버 내에서도 API 형태로 실행 가능)
    • MongoS를 Application Server 서버 local 에 설치하는 것을 추천
      (application server 가 별도의 라우터를 네트워크 공유 안하고, Local에서 직접 접근하기 때문에 효율성 증가. 별도의 서버를 구축 하지 않아서 서버 비용 절감. 단, 관리 포인트로 인한 문제점도 존재)

  • Config 서버로부터 Meta-Data를 Caching
    • read, write 작업시 해당 샤드를 찾을 수 있도록 캐쉬할 수 있는 기능을 제공
root@7d536b10b886:/# mongos --configdb config-replica-set/mongo1:27019,mongo2:27019 --bind_ip 0.0.0.0

 

  • MongoS가 실행될 때 Config 서버를 등록
    • Config 서버와 연결되면 샤딩 정책을 포함한 메타 정보를 연결된 모든 Config 서버에 전송
  • MongoS는 Config Server 와 연결하게 되는데, Config Server가 여러 대인 경우 여러 대 중 하나라도 연결이 안되면 MongoS 는 연결 실패로 MongoS가 실행되지 않음
  • MongoS 내에서는 데이터를 저장하지 않으며, 다른 MongoS간 연결이 없기 때문에 데이터 동기화를 위해 Config 서버를 이용
  • MongoS 의 쿼리 클러스터 라우팅 방법
    1. 쿼리를 보내야 하는 샤드 리스트를 결정
    2. 대상되는 샤드에 커서를 설정
    3. Target Shard에 보낸 결과를 받아 데이터를 병합하고 해당 결과를 Client로 Return
    4. Mongo3.6에서는, 집계 쿼리의 경우 각 Shard에서 작업하는 것이 아닌, Mongos에서 결과를 받아 merge 후 작업하여 리턴하는 형태로 변경
    5. MongoS에서 Pipline을 운영할 수 없는 2가지 경우
      1. 분할 파이프라인의 병합 부분에 Primary shard 에서 동작해야하는 부분이 포함되어 있는 경우
        • $lookup 집계가 실행중인 Shard Collection 과 동일한 Database 내에 있는 unshared collection과 진행 된다면, 병합은 Primary shard에서 실행해야 함
      2. 분할 파이프라인의 $group과 같은 Temporary data를 Disk에 기록해야 하는 경우가 포함된 경우, 또한 Client는 allowDiskUse를 True 지정했을 경우 Mongos를 사용할 수 없음
      3. 이런 경우, Merged 파이프라인은 Primary shard에서 하지 않고, 병합 대상인 샤드들 중 무작위로 선택된 샤드에서 실행
      • Shard cluster 에서 Aggregation 쿼리가 어떻게 동작하고 싶은지 알고 싶으면, explain:true 로 설정하여 aggregation을 실행하여 확인 가능
      • mergeType은 병합 단계에서 ("primaryShard", "anyShard", or "mongos") 로 보여주며,  분할 파이프라인은 개별샤드에서 실행된 작업을 리턴.
      • https://docs.mongodb.com/manual/core/sharded-cluster-query-router/ 참고

 

 

Chunk

  • collection을 여러 조각으로 파티션하고 각 조각을 여러 샤드 서버에 분산해서 저장하는데, 이 데이터 조각을 Chunk라고 함
  • chunk는 각 샤드서버에 균등하게 저장되어야 좋은 성능을 낼 수 있음
  • 균등하게 저장하기 위해 큰 청크를 작은 청크로 Split 하고 청크가 많은 샤드에서는 적은 샤드로 Chunk Migration 을 수행
  • 청크 사이즈는 Default 64Mb 이며 size를  변경도 가능 (또는 100,000 row)
    • Chunk size 변경 시 유의사항
      1. Chunk size를 작게하면 빈번한 마이그레이션이 동작하여 성능은 저하가 발생할 수 있으나, 데이터를 고르게 분배할 수 있는 효과를 볼 수 있다. (추가로 mongos에서 추가 비용이 발생)
      2. 청크 사이즈를 크게하면 마이그레이션 빈도는 줄어들어 네트워크나 mongos 에서 오버헤드 측면에서 효율적.그러나 잠재적으로 분포의 불균형이 발생 가능성
      3. 청크 사이즈는 마이그레이션할 청크 내 document 수와 비례(청크가 클수록 저장되는 document 수가 증가)
      4. 청크 사이즈는 기존 Collection을 분할할 때 최대 컬렉션 크기에 따라 영향. 샤딩 이후 청크 사이즈는 컬렉션 크기에 영향 없음
  • Chunk Split
    • 샤드 내 Chunk의 사이즈가 너무 커지는 것을 막기 위해 split 이 발생
    • 청크가 지정된 청크 사이즈를 초과하거나, 청크내의 문서 수가 마이그레이션할 청크당 최대 문서 수를 초과할 경우 발생
    • 이 때 split는 샤드 키 값을 기준으로 진행
    • Insert 또는 update 시 split 이 발생
    • split 이 발생하면 메타 데이터 변경이 발생하며, 데이터의 균등함을 가져온다
    • Split 을 한다고 해도 청크가 샤드에 고르게 분포되지 않을 수 있음
      • 이때 밸런서가 여러 샤드에 존재하는 청크를 재분배
      • 클러스터 밸런서 참고
  • Chunk Migration
    • 여러 Shard 로 나누어진 청크를 샤드간 균등하게 분배하기 위하여 Migration 을 진행
      1. Balance 프로세스가 moveChunk 명령을 Source 샤드로 명령(Chunk Migration이 필요한 Shard=Source Shard)
      2. Source Shard는 moveChunk 명령으로 이동 시작
        • 이동하는 Chunk는 라우팅 되어 동작하며, 경로에 대한 내역은 Source Shard에 저장(수신에 대한 내역)
      3. Target Shard는 해당 Chunk 관련한 Index를 생성(build)
      4. Target Shard는 Chunk 내의 Document 요청을 하고, 해당 데이터 사본을 수신 시작
      5. Chunk에서 최종(원본) Docuemnt를 수신한 후 Migration 간 변경된 내역이 있는지 확인하기 위하여 다시 한번 Target Shard는 동기화 프로세스를 시작
      6. 완전히 동기화가 완료되면, Config Server에 연결하여 Cluster Meta 데이터를 청크의 새 위치로 업데이트 진행(mongoS가 관여 하지 않을까....의견)
      7. Source Shard가 Meta 정보를 업데이트를 완료하고 Chunk에 열린 커서가 없으면 Source Shard는 Migration 대상을 삭제 진행
    • 샤드에서 여러 청크를 마이그레이션 하기 위해서는 밸런서는 한 번에 하나씩 청크를 마이그레이션 진행
      • 3.4에서부터는 병렬 청크 마이그레이션을 수행 가능. 단, 샤드가 한 번에 최대 하나만 참여하지만, 샤드가 n 개인 여러개의 샤딩된 클러스터의 경우 최대 샤드 n개/2 만큼 동시 Migration을 진행 가능
        • 클러스터 내 Chunk Migration은 한번만 가능했지만, 3.4부터는 Cluster 내 최대 n/2 개의 Chunk Migration은 가능(단 하나의 샤드당 하나의 Chunk Migration만 가능)
    • 하지만, 밸런서는 청크를 이동 후 이동한 청크를 삭제에 대해서(삭제단계)는 기다리지 않고 다음 마이그레이션 진행 (비동기식 청크 마이그레이션 삭제)
    • attemptToBalanceJumboChunks 라는 밸런서 설정을 하면, 마이그레이션 하기 너무 큰 청크는 밸런서가 이동 시키지 않음
    • Migration 조건
      • 수동
        • 대량 Insert 중 데이터를 배포해야 하는 경우 제한적으로 사용
        • 수동 마이그레이션 참조
      • 자동
        • 밸런서 프로세스가 Collection 내 Chunk들이 파편화가 발생하여 고르게 분포 되지 않았다고 판단 될 때 Chunk를 이동 시킴
        • 측정 임계값
          • 하나의 샤드 서번에 8개의 Chunk가 발생하면 다른 서버로 Migration이 발생하는데 20개 미만의 Chunk가 발생하면 평균 2번 정도의 Migration 이 발생
          • Migration이 빈번하게 발생하면 Chunk를 이동하기 위한 작업이 수시로 발생하기 때문에 성능 지연현상을 발생 시킬 수 있음
          • 적절한 빈도의 Migration이 발생되기 위해서는 적절한 Chunk Size를 할당이 필요

Chunk 수

Chunk Migration 수

1 ~20개 미만

2

21 ~ 80 개 미만

4

80개 이상

8

 

Sharded Cluster Balancer

https://docs.mongodb.com/manual/core/sharding-balancer-administration/#sharding-internals-balancing

  • 각 Shard의 Chunk 수를 모니터링하는 백그라운드 프로세스
  • 샤드의 청크 수가 Migration 임계치 값에 도달 하면 밸런서가 샤드간에 Chunk를 자동으로 Migration하고 샤드 당 동일한 수의 청크를 유지
    • 샤드된 컬렉션의 청크를 모든 샤드 컬렉션에 균등하게 재분배하는 역할(밸러서는 Default로 enable)
    • 샤드간 청크가 균등하게 유지될 때까지 밸런서가 동작 (Migration 는 위의 Chunk Migration 동작을 참고)
  • 밸런싱 절차는 사용자 및 어플리케이션 계층에서 투명하게 동작하지만, Migration이 진행되는 동안에는 부하가 발생
  • 밸런서는 Config server Replica set에서 Primary에서 동작
  • 유지보수를 위해 밸런서를 비활성화도 가능하며 수행 시간을 설정도 가능
  • balancing 작업이 02:00 에 자동으로 수행
#Config 서버에서 동작을 확인 가능

repl_conf:PRIMARY> db.settings.update (

... {_id:"balancer"},

... {$set : {activeWindow: {start: "02:00", stop : "06:00" } } },

... {upsert : true } )

WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : "balanver" })

repl_conf:PRIMARY> db.settings.find()

{ "_id" : "chunksize", "value" : 64 }

{ "_id" : "balancer", "stopped" : false }

{ "_id" : "autosplit", "enabled" : true }

{ "_id" : "balanver", "activeWindow" : { "start" : "02:00", "stop" : "06:00" } }
  • 샤드 추가 및 제거
    • 샤드를 추가하게 되면 새로운 샤드에는 Chunk가 없기 때문에 불균형이 발생
    • 클러스터에서 샤드를 제거하면 상주하는 청크가 클러스터 전체로 재분배가 일어나므로 샤드 추가와 같이 불균형이 발생
    • 샤드를 추가하거나 삭제 모두 마이그레이션 하기 시작하면 시간이 소요
    • 효율적인 방안은 좀 더 조사가 필요

 

Sharding System 주의점

  1. 하나의 Shard 서버에 데이터가 집중되고 균등 분산이 안 되는 경우
    • Shard Key로 설정된 필드의 Cardinality가 낮은 경우에 Chunk Size는 반드시 64Mb 단위로 분할되는 것이 아님
      • 때로는 64Mb보다 훨씬 큰 크기의 Chunk 크기로 생성되기도 함
      • Default로 8개 정도의 Chunk가 발생하면 Migration이 발생하기 때문에 다른 서버로 Chunk를 이동하는 횟수는 줄어들게 되고 자연스럽게 하나의 샤드 서버에 만 데이터 집중되는 현상 발생
    • 적절한 Shard Key를 선택하지 못한 경우 발생하는 문제점(Shard Key의 중요성)
    • 혹여나 균등 분산이 안된다고 판단되면 Chunk Size를 줄이는 것을 추천(Migration 빈도수가 높아 져 균등하게 분활은 되나 성능 저하 발생)
  2. 특정 Shard 서버에 IO 트래픽이 증가하는 경우
    • MongoDB의 샤드 서버는 동일한 Shard Key를 가진 데이터들을 같은 샤드 서버에 저장하기 위해 Split 과 Migration 수행
    • Shard 서버의 IO 트래픽이 증가하는 이유는 너무 낮은 Cardinality 를 가진 Field를 설정 때문
    • 데이터가 집중적으로 저장되어 있는 Chunk를 Hot Chunk라고 하며, 특정 서버에 집중되어 있을 때 상대적으로 서버 IO 트래픽이 증가
  3. 샤드 클러스터의 밸런스가 균등하지 않는 경우
    • 데이터를 입력할 때 로드 밸런싱이 적절하게 수행되었지만 사용자의 필요에 따라 특정 서버의 데이터를 삭제 또는 다른 저장 공간으로 이동했다면 Balancer가 깨지게 됨
    • Shard key 의 낮은 Cadinality
    • 하나의 Collection에 대량의 Insert 되는 것 보다 분산 저장되는 속도가 늦는 경우 밸런스 불균형 발생
    • Insert 되는 속도에 비해 Chunk Migration이 빈약한 네트워크 대역폭으로 인해 빠르게 이동되지 못하는 경우 발생
    • 하루 일과 중 Peak 시간에 빈번한 Migration이 발생하게 되면 시스템 자원의 효율성이 떨어지게 되어 성능 지연 발생
      • 피크 시간에 Chunk Migration을 중지하고 유휴 시간에 작업 될 수 있도록 Balance 설정
  4. 과도한 Chunk Migration이 클러스트 동작을 멈추는 경우
    • 빈번한 Chunk Migration이 일시적으로 Cluster 서버 전체의 성능 지연 문제를 유발
    • 빈번한 Migration 회수를 줄일 수 없다면 유휴 시간에 작업 될 수 있도록 Balance 설정
    • 불필요하게 큰 Chunk Size는 네트워크 트래픽을 증가시키고 시스템 자원의 효율성을 저하 시키는 원인이 될 수 있으므로 Chunk size를 줄이는 것 고려
    • Shard 서버의 밸런싱이 적절하지 않다는 것은 Shard 서버의 수가 처리하려는 데이터 발생 양에 비해 부족하기 때문이므로 샤드 서버 대수 증설 고려
  5. 쓰기 성능이 지연되고 빠른 검색이 안 되는 경우
    • 초당 몇 만 건 이상의 데이터들이 동시에 저장되기 위해 Collection의 크기가 중요
    • 하나의 Collection은 여러개의 익스텐트 구조로 생성되는데, 익스텐트 사이즈가 작게 생성되어 있으면 잦은 익스텐트 할당으로 인해 불필요한 대기 시간이 발생
    • 충분한 익스텐트 크기로 생성(createCollection 을 이용하여 Collection을 명시적으로 생성하면서 size를 조정 가능)
    • 빠른 쓰기 성능이 요구되는 경우 Rich Docuemnt 구조로 설계하는 것이 유리  (Data Model Design 참고 /  Embedded Document도 고려)
    • 메모리 부족으로 인한 성능 저하로 메모리 증설도 고려
  • Shard Key 의 중요성,(1~5) Balancer는 유휴 시간대에 작업 추천(3), 적절한 Shard 수(4), 메모리도 체크(5)

참고

반응형

[MongoDB] 웨비나 - mongodb 에 대한 8가지 오해

  1. 작년 10월 에  MongoDB 웨비나에서 소개된 자료를 정리한 것이 있어서 직접 다시 집중적으로 정리하였습니다.
  • 소개

https://www.mongodb.com/presentations/korea-top-misunderstanding-of-mongodb

  • 비디오

https://vimeo.com/468475015

  • PPT

https://view.highspot.com/viewer/5f882ff5a2e3a96b5627dc39

 

[ WiredTiger Engine ]

 

  • 2014 년 Wired Tiger 업체를 인수
  • 3.0 에서 MMAPv1 -> WT 으로 변경 진행 (MMAPv1 4.0 이하에서는 사용 가능 / 4.2에서는 제거)\
  • 본질적으로 Transaction Storage Engine 이며, Read & Write 에 우수(MMAPv1 은 Insert 에 최적화된 Engine)
  • 4.0 에서 부터는 분산 Multi Transaction 을 지원
  • 특징
    • WiredTiger에서 압축은 기본 옵션
    • v4.2 부터 총 3개의 압축 옵션을 제공 (snappy, zlib, zstd)
      • 호환성 성능상 아직 이슈가 있어서 snappy 이용
      • 압축률은 zstd(zlib와 대등) 가 더 좋으나, 성능은 snappy 와 zlib 사이로 판단
    • 인덱스는 prefix 압축을 사용
      • 데이터는 WT의 Cache 상태에서는 non 압축 상태 즉, 데이터를 저장 시 Document로 bson 형태로 저장되는데, bson 은 non압축상태로 WT cache 에 존재하며 Disk 와 os file cache 에는 압축 상태로 저장.
      • 만약 find 하는 경우 먼저 cache 의 non 압축 상태에서 먼저 확인 후, Disk 의 압축 상태에서 찾아봐 압축을 해제하며 cache 에 적재
      • prefix 압축이란?
      • 그런데 Index 는 메모리 상 뿐만 아니라 디스크에도 prefix 압축된 상태로 사용(메모리의 효율성을 증대 가능)
    • 삭제된 공간을 재활용 못하기 때문에 Disk size가 무한대로 증가하는 과거 버전과 달리, 현재는 재활용 가능
    • 그래서, collection 에서 사용되지 않는 공간을 확인 가능하며, WT 에서 수동으로 compaction 을 할 수 있음
    • DDL 작업을 제외하고는 4.4 부터 온라인 Compaction 할 수 있음
      • 기존에는 Database level lock 을 잡는것과 달리, 4.4 부터는 CRUD 작업에 대해서는 Lock을 잡지 않음.
      • 그래서 운영간 Primary에서도 가능
    • 초기 버전에는 Global -> Database -> collection Level Lock 을 잡았음 (몇 백개의 Collection이 존재하는 과거 운영)
    • 현재는 Document Level Locking
    • Write no longer block all other writes
    • More CPU cores, instead of faster CPU speeds (하드웨어의 자원을 최대로 사용 가능 - 성능 향상 가능 - 고 사양 서버를 사용 가능하며 유리)
    • Avoid need to "micro-shard"
    • WT에서는 메모리 Cache size , Check point Interval time 등을 조정 가능 (파라메터 튜닝 가능)
    1. 압축
    2. 온라인 Compaction
    3. 향상된 Concurrency
    4. 최신 하드웨어 활용
    5.  조정 가능한 설정 파라메터
  • MMAPv1 와 비교한 레퍼런스 (amadeus , book my show, FaceIT 등의 글로벌 업체)
    • 스토리지 공간을 80% 절감
    • 최대 5~10 배 향상된 성능

[암호화]

  • 3.6 부터 Binding 을 Localhost 가 Default 로 되어 있어서, 설치 시 Localhost 에서만 접속 가능
  • 아래 내역들에 대해 Community 와 Enterprise 등 모두 사용 가능

 

[Check Point]

  • 스토리지 상의 일관된 데이터 페이지 셋으로, 내구성 / 원자성을 지닌 디스크의 데이터 스냅 샷이라고 정의
  • 쉽게 말하면, 데이터를 DML 하는 가운데 메모리 Page상으로 존재하는데, 매 1분 또는 메모리 Dirty 영역이 일정 임계치 이상이 되는 경우 해당 Page들을 Disk로 저장

 

[Journaling]

  • 체크 포인트 간의 발생하는 모든 데이터 수정 사항을 신속하게 유지할 수 있는 write-ahead 로그 (journal)
    • write-ahead 로그 : 디스크에 쓰기 전에 한번 더 별도의 공간에 저장 (파일시스템 내에 저널파일이-100mb 단위/ 존재하며 체크 포인트 발생하면 모두 삭제 됨)
    • 데이터 뿐만 아니라 인덱스 에서 발생하는 로그들 모두 기록
  • 저널링 버퍼가 존재하는데, 저널링 버퍼에서 저널링 파일로 저장되는 시간이 1oms  (10ms 의 유실 가능성 존재)
  • 체크 포인트 간에 장애가 발생하면 저널을 사용하여 마지막 체크 포인트 이후 수정 된 모든 데이터를 적용

* Journaling 없이도 MongoDB는 손실 없이 마지막 체크 포인트로 복구 가능하나, 마지막 체크 포인트 이후의 변경 사항을 복구하려면 저널링을 활성화해야 가능

 

[Write Concerns]

  • Write 작업을 위해 MongoDB cluster에서 Client Application Ack 요청을 제어
  • Write 작업을 할때 Primary 에 적용 하고 응답을 받고 Session 을 종료 할 것인지, Secondary 까지 적용이 완료 된 후 응답을 받아 종료를 할것인지, Primary 에서 조차도 응답을 안받고 작업을 종료 할 것인지에 대한 Level 제어
  • 또한 wtimeout 을 적용하여 해당 시간 보다 오래 소요되는 경우 Error return (단위 ms)
  • Write Concern Option
    • 1 : Written to Primary - Default
    • 2 : written to Primary and at least one Secondary
    • majority : Written to majority of nodes
  • ex) 과반수 노드가 write 된 후에 ack 를 달라고 하는데, 단 5초 이상 소요 시 error return
  • db.products.insert({name : "Louis.Kim"}, {writeConcern:{w:"majority", wtimeout:5000}})

[Multi-Document Distributed ACID Transactions]

  • Single/ Multi Document, Shard  등 Transaction 지원
  • RDBMS와 동일한 형태
    • Transaction Default Timeout 60초
    • 다중 구문, 유사한 문법
    • 어플리케이션에 쉽게 추가 가능
    • 여러 컬렉션에 대한 다중 구문 트랜잭션
  • ACID 보장
    • All or nothing execution
    • Snaphost isolaction ( Serializable Snapshot Isolaction의 공동 발명자인 Michael Cahill 포함한 Wired Tiger 회사 인수-와 연관 )

Causal Consistency

반응형

+ Recent posts