1.1 Process 영역
- Client Process : Mongo.exe를 실행시켜서 MongoDB 에 접속할 때 활성화되는 Process
- Server Process : Mongod.exe를 통해 MongoDB를 시작할 때 활성화되는 Process
1.2 Memory 영역
- Memory Cache : Data Read/Write에 사용되는 영역
- Journal 영역 : Read/Write 작업을 Backup 해두는 영역, 예기치 못한 장애 대비
- Resident (WorkSet) : 실제 Data가 처리되는 영역
1.3 File 영역
- Datafile : 실제 Data가 최종 저장되는 물리적 공간입니다.
*.NS (namespace) , *.숫자 라는 이름을 가진 File들로 저장되며, *는 Database의 이름으로 저장됩니다.
- Journal File : DML 작업 수행시 가장 먼저 Journal 영역으로 저장되고, 그 다음 Datafile에 반영됩니다.
- Logfile : 추가 환경 설정을 통해서 Log 정보를 파일로 남길 수 있습니다.
2. 물리적 구조 ( File 구조)
- .ns (namespace) 파일은 최초 생성시 x86은 16MB , x64는 32MB로 생성됩니다.
- .숫자 (datafile) 파일은 최초 생성시 x86은 32MB , x64는 64MB로 생성됩니다.
- 위 2 File 모두 x86은 32MB , x64는 64MB의 배수의 크기로 확장됩니다.
- File의 크기가 2GB까지 커지면 추가로 파일을 생성합니다.
- Namespace File에는 Collection의 First Extent , Last Extent , Meta Data , Index 정보, FreeList 정보가 저장됩니다.
- Journal File은 MongoDB가 동작하는 동안 유지되며, 모든 DML ( INSERT ,UPDATE , REMOVE ) 작업을 할 때마다 크기가 커집니다.
- Journal File의 최대 크기는 1GB이며, 필요시 File이 추가로 생성됩니다.
- MongoDB Server 종료시 Journal File은 삭제됩니다.
- 비정상적으로 MongoDB Server가 종료되었을 경우 Journal File이 존재하고 있는 상태가 되며, 그때는 Server 가동시 Journal File을 통해서 복구 작업을 수행 합니다.
Journal 을 활성화해서 실행 시키는 방법은 다음과 같습니다.
mongod --dbpath c:/mongodb/test --journal 2015-08-11T08:57:34.308+0900 I JOURNAL [initandlisten] journal dir=c:/mongodb/test\journal 2015-08-11T08:57:34.310+0900 I JOURNAL [initandlisten] recover : no journal files present, no recovery needed 2015-08-11T08:57:34.334+0900 I JOURNAL [durability] Durability thread started ... 2015-08-11T08:57:34.336+0900 I JOURNAL [journal writer] Journal writer thread started ... 2015-08-11T08:57:34.338+0900 I CONTROL [initandlisten] options: { storage: { dbPath: "c:/mongodb/test", journal: { enabled: true } } } ... |
기본적으로는 MongoDB 실행시 지정한 --dbpath에 Datafile들이 생성되는데, Database 별로 별도로 생성 할 수도 있습니다.
--directoryperdb : DB별로 별도의 Directory에 File을 생성합니다.
--nssize : Namespace File의 최초크기 MB 단위
--guatoFiles : Datafile의 최대 생성 개수
하지만 이미 해당 옵션들로 생성되지 않은 --dbpath에는 적용이 안됩니다.
mongod --dbpath c:/mongodb/test --directoryperdb --nssize 100 --quotaFiles 10 2015-08-11T09:03:09.033+0900 I STORAGE [initandlisten] exception in initAndListen: 72 Requested option conflicts with current storage engine option for directoryPerDB; you requested true but the current server storage is already set to false and cannot be changed, terminating 2015-08-11T09:03:09.035+0900 I CONTROL [initandlisten] dbexit: rc: 100 |
새로운 Folder(c:/MongoDB/orange)를 생성 한 후 다시 시도해 보겠습니다.
mongod --dbpath c:/mongodb/orange --directoryperdb --nssize 100 --quotaFiles 10 ... 2015-08-11T09:04:16.216+0900 I CONTROL [initandlisten] options: { storage: { dbPath: "c:/mongodb/orange", directoryPerDB: true, mmapv1: { nsSize: 100, quota: { maxFilesPerDB: 10 } } } } 2015-08-11T09:04:16.219+0900 I INDEX [initandlisten] allocating new ns file c:/mongodb/orange\local\local.ns, filling with zeroes... 2015-08-11T09:04:17.191+0900 I STORAGE [FileAllocator] allocating new datafile c:/mongodb/orange\local\local.0, filling with zeroes... 2015-08-11T09:04:17.192+0900 I STORAGE [FileAllocator] creating directory c:/mongodb/orange\local\_tmp 2015-08-11T09:04:17.202+0900 I STORAGE [FileAllocator] done allocating datafile c:/mongodb/orange\local\local.0, size:64MB, took 0.003 secs 2015-08-11T09:04:17.252+0900 I NETWORK [initandlisten] waiting for connections on port 27017 |
이제 Client로 접속 한 후 추가로 Database들을 생성해보겠습니다.
C:\>mongo > use test > db.test.insert ( { id : 1 } ) > use orange > db.test.insert ( { id : 1 } ) > exitC:\>cd/MongoDB/Orange/Orange C:\MongoDB\orange\orange>dir ... 2015-08-11 오전 09:13 67,108,864 orange.0 2015-08-11 오전 09:13 104,857,600 orange.ns ... C:\MongoDB\orange\orange>cd ../test C:\MongoDB\orange\test>dir ... 2015-08-11 오전 09:13 67,108,864 test.0 2015-08-11 오전 09:13 104,857,600 test.ns ... |
3. 논리적 구조
하나의 Database 내에는 여러개의 Collection이 있으며, Collection은 Extent들로 구성되어 있습니다. Extent는 여러 개의 Document들을 저장 합니다. 실제로 Document는 Data Record 안에 들어있는데, Data Record는 Document 1개와 필요한 부가 정보 몇가지를 포함하고 있습니다. (Oracle과 그 구조가 별반 다르지 않습니다. Oracle의 경우는 Extent 내에 Block들이 있고, Block 내에 Record들을 저장합니다.)
Database > Collections > Extents > Data Record = Document
Collection 생성시 Extent 기본 Size는 8K로 설정됩니다.
(createCollection 으로 생성하던, insert, save 로 생성하던 똑같습니다.)
size를 별도로 8K 이하로 지정하더라도 8K로 생성됩니다.
db.createCollection("e" , { crapped : false , size:4096 } ) db.e.validate() { "ns" : "test.e", "datasize" : 0, "nrecords" : 0, "lastExtentSize" : 8192, "firstExtent" : "0:4eb000 ns:test.e", "lastExtent" : "0:4eb000 ns:test.e", "extentCount" : 1, "firstExtentDetails" : { "loc" : "0:4eb000", "xnext" : "null", "xprev" : "null", "nsdiag" : "test.e", "size" : 8192, "firstRecord" : "null", "lastRecord" : "null" }, ... } db.d.insert( { id : 1 } ) db.d.validate() { "ns" : "test.d", "datasize" : 48, "nrecords" : 1, "lastExtentSize" : 8192, "firstExtent" : "0:4a3000 ns:test.d", "lastExtent" : "0:4a3000 ns:test.d", "extentCount" : 1, "firstExtentDetails" : { "loc" : "0:4a3000", "xnext" : "null", "xprev" : "null", "nsdiag" : "test.d", "size" : 8192, "firstRecord" : "0:4a30b0", "lastRecord" : "0:4a30b0" }, ... } |
3.1 Extent 와 Data Record
- Extent는 Data가 저장되는 논리적 단위입니다.
- Extent의 크기가 Read/Write의 성능에 큰 영향을 미칩니다.
만약 Big Data의 입력이 자주 일어나는 Collection의 Extent 크기가 너무 작다면 입력 도중 새로운 Extent를 생성하는 회수가 많아져서 결국 쓰기 작업의 성능 지연을 발생시킬 수 있습니다.
- Extent의 구조는 위의 실습 결과를 보면 알 수 있듯이 위치, 앞/뒤 Extent의 주소, Collection명, 크기, Extent내의 처음/마지막 Record 주소 를 가지고 있습니다.
- Data Record는 크기, 앞/뒤 Data Record의 주소, Document 를 가지고 있습니다.
for (var i = 0; i < 65535; i++) db.big.insert( { id : i , name : "test" } ) db.big.validate( { full : true } ) { "ns" : "test.big", "datasize" : 7339920, "nrecords" : 65535, "lastExtentSize" : 8388608, "firstExtent" : "0:50d000 ns:test.big", "lastExtent" : "0:a57000 ns:test.big", "extentCount" : 6, "extents" : [ { "loc" : "0:50d000", "xnext" : "0:52f000", "xprev" : "null", "nsdiag" : "test.big", "size" : 8192, "firstRecord" : "0:50d0b0", "lastRecord" : "0:50ef30" }, { "loc" : "0:52f000", "xnext" : "0:50f000", "xprev" : "0:50d000", "nsdiag" : "test.big", "size" : 32768, "firstRecord" : "0:52f0b0", "lastRecord" : "0:536f30" }, { "loc" : "0:50f000", "xnext" : "0:557000", "xprev" : "0:52f000", "nsdiag" : "test.big", "size" : 131072, "firstRecord" : "0:50f0b0", "lastRecord" : "0:52ef30" }, ... { "loc" : "0:a57000", "xnext" : "null", "xprev" : "0:657000", "nsdiag" : "test.big", "size" : 8388608, "firstRecord" : "0:a570b0", "lastRecord" : "0:fad4b0" } ], ... "objectsFound" : 65535, "invalidObjects" : 0, "nQuantizedSize" : 65535, "bytesWithHeaders" : 8388480, "bytesWithoutHeaders" : 7339920, "bytesBson" : 3211215, "deletedCount" : 6, "deletedSize" : 2792544, ... "nIndexes" : 1, "keysPerIndex" : { "test.big.$_id_" : 65535 }, ... } |
3.2 Index Record
- V2 이하 버전이나 V3이상중 NMAP을 Storage Engine으로 사용하는 경우에는 Index Record 또한 Extent 내에 저장되고, 해당 Extent는 .ns 이름을 가지는 namespace 파일 내에 생성됩니다. 그럴 경우에는 생성할 Index의 개수와 크기를 고려하여 Namespace File의 크기를 정하는 것이 중요합니다. 이 경우 db.collection.index.validate( { full : true } ) 명령어로 Table과 같이 Extent 정보 확인이 가능합니다.
- V3 이상 버전의 나머지 Storage Engine (rocksdb, wt, ...)들은 공식적으로는 diskLoc 라는 것으로 Data 위치를 관리합니다. diskLoc 관련 정보를 보는 명령어는 db.collection.find().showDiskLoc() 라는 명령어로 Collection 의 정보 확인이 가능하며, Index의 경우에는 db.system.indexes.find().showDiskLoc() 로 확인이 가능합니다.
db.big.createIndex( { id : 1 } ) db.big.stats() { "ns" : "test.big", "count" : 65535, "size" : 7339920, "avgObjSize" : 112, "numExtents" : 6, "storageSize" : 11182080, "lastExtentSize" : 8388608, "paddingFactor" : 1, "paddingFactorNote" : "paddingFactor is unused and unmaintained in 3.0. It remains hard coded to 1.0 for compatibility only.", "userFlags" : 1, "capped" : false, "nindexes" : 2, "totalIndexSize" : 3785488, "indexSizes" : { "_id_" : 2125760, "id_1" : 1659728 }, "ok" : 1 } db.system.indexes.find({ ns : "test.big" }).showDiskLoc().pretty() { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.big", "$diskLoc" : { "file" : 0, "offset" : 1420976 } } { "v" : 1, "key" : { "id" : 1 }, "name" : "id_1", "ns" : "test.big", "$diskLoc" : { "file" : 0, "offset" : 1421104 } } |
4. Memory 구조
MongoDB의 Memory 영역은 크게 Virtual Memory 영역과 Resident 영역으로 나눌 수 있습니다.
4.1 Virtual Memory Area
- Mapped Cache Area : Data가 Cache될 Memory Area
- Virtual Area : Cache Area가 부족할 때 사용될 추가 Cache Area
- Journal Area : 사용자의 작업 내용을 실 시간으로 Backup할 Memory Area
4.2 Resident Area (WorkingSet)
- 실제로 Data를 처리하는 Memory Area
use admin db.serverStatus().mem { "bits" : 64, // Platform Bits "resident" : 168, // Resident Area Size "virtual" : 900, // Virtaul Area Size "supported" : true, "mapped" : 368, // Mapped File Size "mappedWithJournal" : 736 // Journal Memory Size } db.serverStatus().extra_info { "note" : "fields vary by platform", "page_faults" : 137164, // Memory PageFault Count "usagePageFileMB" : 110, "totalPageFileMB" : 10026, "availPageFileMB" : 4467, "ramMB" : 8106 // Memory Size } |
4.3 적정 Memory 요구 사항
- Memory Mapping 기법으로 Data의 Read/Write 작업을 수행하므로 충분한 Memory 공간이 확보되어야 합니다.
Disk상에 별도의 Virtual Memory를 사용하지 않으므로 Datafile의 약 2배 이상의 여유 Memory 공간을 요구합니다.
- Data Read/Write를 위해 실제로 작업하는 영역을 Resident Area (Working Set)이라고 합니다.
작업 발생시 전체 System 영역의 90 ~ 95% 범위까지 할당 받게 됩니다.
- Datafile 의 크기가 2GB인 경우 RAM은 약 10GB ~12GB정도가 요구되며, RAM이 부족한 경우 Flushing 과 PageFault가 발생하여 성능 저하를 일으킬 수 있습니다.
4.4 Journal File
- Data Read/Write 작업에서의 Data 유실을 방지하기 위해서 별도의 File로 저장합니다.
--dbpath 로 정의된 경로에 생성됩니다.
최대 크기는 1GB이며, 여러 개로 구성됩니다.
- 사용자 DML 작업(INSERT , UPDATE, REMOVE)은 Journal File에 먼저 저장됩니다.
장애 발싱 시 Journal File을 이용하여 Datafile을 복구합니다.
- 기본적으로 매 100 ms 마다 100 mb를 저장합니다.
-- journingCommitInterval 옵션을 이용하여 2 ms ~ 300 ms 범위 안에서 정의 할 수 있습니다.
쓰기 시간은 최소로 하면 Data 유실을 방지 할 수 있지만, 그만큼 성능이 저하되므로 적절하게 조정해야 합니다.
- Journal Mode가 요구되는 경우
Single Node : Data 무결성 보장을 위해 반드시 필요합니다.
Replica Set : 최소 1 Node에 정의해야 합니다.
- 각 Databased의 Datafile과 분리된 물리적 Disk에 생성해야 좋은 성능을 기대할 수 있습니다.
--directoryperdb 옵션을 이용하면 각 Database의 Datafile을 분리 할 수 있습니다.
5. Lock Policy
각 Transaction Level 별 Isolation 타입에 대한 내용은 아래 Slide 내용 참조 부탁드리겠습니다.
MongoDB의 경우 V1.8까지는 Global Lock을 제공하였으며,
V2.2 이후부터 Database Lock을 지원합니다.
자세한 내용은 아래 MongoDB 공식 Site를 참고하시기 바랍니다.
http://docs.mongodb.org/manual/faq/concurrency/
댓글 없음:
댓글 쓰기