Building an Inexpensive Petabyte Database with MongoDB and Amazon Web Services: Part 1 | MongoDB Blog
Building an Inexpensive Petabyte Database with MongoDB and Amazon Web Services: Part 2 Picking Back Up When we left off in the previous post , we’d chosen a goal of building a petabyte MongoDB database, evaluated the options for achieving it, and selecte
www.mongodb.com
개요
회사에서 우연히, MongoDB에 1 PB 정도의 데이터를 저장할 수 있는지 산정해달라는 요청을 받았습니다.
개발자분께서 위에 작성된 약 8년전 article을 같이 보내주셨는데, 내용이 흥미로워 소개를 하고 싶었습니다.
요약
결론적으로, article에서는 저렴하게 MongoDB에 1PB를 저장하고, 사용할 수 있다고 말하고 있습니다.
- hs1.8xlarge를 사용하여 인스턴스 당 46TB의 HDD를 사용했으며
- 1PB의 데이터 Load와 성능 테스트를 위해 커스텀된 YCSB를 사용했습니다.
- YCSB는 local에서 실행되며 mongos가 아닌 mongod 프로세스에 직접 붙습니다.
- XFS파일 시스템 + Ubuntu 13.10 + Oracle Java 7 + Global Shell + munin-node monitoring 을 사용합니다.
- 1PB의 데이터를 Load하는데 17 ~ 22시간이 걸렸고 몇몇의 YCSB 프로세스가 일찍 종료되기도 했습니다.
- 1,057,240,224,818,640 bytes 를 저장했고, 인덱스가 생성된 필드를 기준으로 broadcast query가 약 3초 정도 걸렸습니다.
- 50GB/m = 844MB/s 의 쓰기 성능, 읽기는 findOne() 기준 800QPS 정도의 성능을 확인할 수 있었습니다.
개인적인 의견
또한 해당 내용은 MongoDB에 저렴하게 1PB를 저장하는 방식을 소개하고 있을 뿐, 서비스에 투입할 수 있다고 이야기하지는 않습니다. 비용적인 측면에서 1PB를 저장하는 MongoDB의 비용을 검토할 뿐, 성능과 운영에 대한 고려를 전혀 하지 않고 있습니다. 따라서 해당 자료를 기반으로 MongoDB에 1PB를 저장할 수 있다고 주장하는 것은 힘들 것 같습니다.
개인적인 의견으로는 다음 이유로 MongoDB는 1PB 정도의 대용량 데이터를 저장할 용도로 제작된 데이터베이스가 아니라고 생각합니다.
- MongoDB에서는 SSD Disk를 권장합니다. Disk를 SSD를 사용하는 경우 데이터 저장 비용이 매우 비쌉니다.
https://www.mongodb.com/docs/manual/administration/production-checklist-operations/#hardware - 인덱스 생성, 백업 등에 소요되는 운영비용이 매우 비쌉니다.
MongoDB에 1PB를 저장하기 위한 운영 인프라가 잘 갖춰진 경우에는 가능할 것 같다고 생각합니다. 그러나 비용적인 측면에서 검토가 충분히 진행되어야할 것 같습니다.
본문 내용
본문 내용에 들어가기 전, 해당 article은 2014년에 작성된 것임을 알려드립니다.
그리고 MongoDB 2.6 버전을 사용하고 있습니다.
Part 1
저자는, 확장성에 대한 고민과 함께 가능한 한 저렴하게 1 PB 데이터베이스를 구축하는 것을 목표로 했습니다.
이때 말하는 PB란 10^15 바이트를 의미합니다. ( 2^50 인 pebibyte 와 다릅니다. )
스토리지 타입 정하기
해당 테스트는 AWS 상에서 진행되었습니다.
AWS에서 제공하는 disk type에는 세가지가 있습니다.
1. Elastic Block Storage (EBS)
EBS는 MongoDB에 가장 적합한 스토리지 타입이지만,
EBS 볼륨은 최대 1TB에 4000PIOPS로 제한이 있으며
하나의 EC2에 24개의 EBS만 마운트할 수 있는 제한이 있습니다.
따라서, 인스턴스당 제한은 24TB와 96K IOPS ( 24 * 4000 ) 입니다.
Provisioned IOP당 블록 크기는 16KB로, 최대 순차 처리량은 1.5GB/s 입니다.
실제로는 인스턴스의 10Gbps 네트워크 트래픽 제한으로 인해 처리량이 제한될 수 있습니다.
2. Ephemeral SSD - 임시 SSD ( EC2 생성 시 붙는 SSD )
i2.8xlarge 인스턴스는 800GB 용량의 SSD 8개를 제공합니다.
인스턴스 당 총 6.4TB를 제공하고, 4KB 블록 사이즈에 300K IOPS의 성능을 제공합니다.
따라서 순차 처리량은 1.2GB/s 입니다.
( 처리량 = 블록사이즈 * IOPS )
3. Ephemeral Spinning Disk
hs1.8xlarge 인스턴스는 2TB 용량의 SATA 드라이브 24개를 지원합니다.
RAID 0으로 묶는 경우, 총 46TB이며 2.5K IOPS와 2.5 Gbps 처리량을 제공합니다.
** Throughput = IOPS * block size
다음 표로 정리될 수 있습니다.
Instance Type | Capacity (TB) | IOPS (K) | Sequential (GB/s) | Cost: instance and storage ($/hr) | Cost: PB total storage ($/hr) |
r3.8xlarge/EBS | 24 | 96 | 1.5 | 15.63 | 657 |
i2.8xlarge | 6.4 | 300 | 1.2 | 6.82 | 1070 |
hs1.8xlarge | 46 | 2.5? | 2.5 | 4.6 | 101 |
스토리지 선택 시 고려사항은 가격뿐만이 아닙니다.
스토리지 선정 시 다음과 같은 AWS에서의 여러가지 제한사항을 고려해야합니다.
- EBS PIOPS 는 기본적으로 계정 당 40K PIOPS 제한이 존재합니다.
인스턴스 당 24TB와 96K PIOPS를 사용하는 경우, 42개의 인스턴스가 필요합니다. ( 24 * 42 = 1008 > 1PB ) 이는 총 4M(4,000,000) PIOPS이므로, 이 정도 수준의 PIOPS를 사용하는 경우 AWS와의 협의가 필요하다고 이야기하고 있습니다. - SSD 인스턴스는 또한 가용영역에 제한사항이 있습니다.
i2.8xlarge를 사용하는 경우 160개의 인스턴스를 사용해야하는데 ( 6.4TB * 160 = 1024TB > 1PB ) AWS 측에서 1개의 계정에 160개의 인스턴스를 사용할 수 있게 허용했지만, 단일 region에서는 특정 시간에 가용가능한 서버가 충분하지 않았습니다. - hs1.8xlarge 인스턴스를 약 30개 프로비저닝 하는데에는 큰 문제가 없었습니다. 1 PB 데이터를 bulk load 하기 위해 24시간동안 충분히 가용가능했고, 가격도 2500$ 정도였습니다.
따라서, 결론적으로 원문에서는 hs1.8xlarge 인스턴스를 사용합니다.
Data Source 선택하기
MongoDB에 1 PB의 데이터를 적재하기 위한 Data Source도 선택해야했는데요. 저자는 Yahoo Cloud Serving Benchmark(YCSB)를 선택했습니다. YCSB는 다양한 종류의 데이터베이스를 테스트하기 위해 널리 쓰이는 도구입니다. 모든 데이터베이스에 비슷한 방식으로 동작하기 때문에 MongoDB를 위한 테스팅은 진행할 수 없지만, 쉽게 사용가능하고 multi threading을 잘 제공하기 때문에 load를 생성하기 편합니다. 이에 YCBS의 기본 버전을 조금 수정하여 테스트에 사용합니다.
YCBS의 Achille Brighton의 포크 버전을 사용합니다. Achille Brighton 버전은 MongoDB Java driver에 대한 업데이트, connection pool 조절 등 여러가지 업데이트에 대해 꾸준히 관리되었기 때문입니다.
또한 YCBS에 몇가지 사항을 추가 적용했습니다.
- 대용량 documents 를 사용하더라도, 20,000,000,000 (20억개) 의 documents 사용해야하기 때문에 documents 수를 줄이기 위해서 document id를 long Integer를 사용하도록 수정했습니다.
- 유의미한 Aggregation 의 성능 측정을 위해, long-tail 분포 또는 zipfian 분포로 임의 생성된 8개의 정수 필드를 추가했습니다.
YCBS Client는 샤드의 Primary 가 수행중인 서버의 local에서 실행됩니다. 이를 통해 네트워크 홉을 줄일 수 있습니다.
Cloud와 System Specifics
Network 최적화 ( low latency )를 위해 단일 가용영역을 선택합니다. 모든 서버를 AWS Placement Group에 위치시키는 것은 네트워크 최적화를 위해 좋지만, 하나의 Placement Group에는 30개의 서버를 위치시킬 수 없습니다.
** 랙 분산형 배치 그룹은 각 그룹의 가용 영역당 실행 인스턴스를 최대 7개까지 지원합니다. 예를 들어 가용 영역이 3개인 리전에서는 그룹에서 총 21개의 실행 인스턴스를 실행할 수 있습니다(영역당 7개).
해당 테스트에서는 데이터의 양과 속도에 대한 POC이기 때문에 24개의 Disk를 RAID0으로 사용하고, MongoDB 공식문서에서 권장하는 방식으로 128MB의 log size로 XFS 파일시스템을 사용합니다. 만약 운영목적이라고 하면 RAID10으로 사용하고, 이에 디스크 요구사항이 2배가 될 것입니다.
OS는 XFS파일 시스템 + working memory cgroup을 제공하는 Ubuntu 13.10 을 사용하고. Oracle Java 7과 모든 Cluster에 명령을 내리기 위한 Global Shell + munin-node monitoring + Data Loading을 위해 수정된 YCSB를 사용합니다.
MongoDB Specifics
spinning SATA disk는 약 100정도의 낮은 랜덤 IOPS 성능을 보입니다. 하지만 RAID0인 24개의 SATA disk와 함께라면 성능은 24배인 2.4K IOPS가 됩니다. 해당 디스크를 최적으로 사용하기 위해서, 4개의 MongoDB 샤드를 사용합니다. mongod 프로세스와 YCSB 프로세스가 같은 서버에서 돌기 때문에, 두 프로세스간 경쟁을 방지하기 위해 해당 프로세스들을 linux cgroup을 사용해 격리합니다.
샤드 자체는 샤드당 하나의 청크로 사전 분할되어 균등하게 분산되어 있었습니다. 그 후 스크립트를 통해 밸런서 프로세스를 켜고, 청크와 샤드가 1:1이 되는 것을 확인한 뒤 밸런서를 껐습니다. AutoSplit을 off로 설정하고 mongos router를 구동했습니다.
네트워크 오버헤드를 줄이기 위해서 YCSB를 local에서 실행하고, mongos가 아닌 각 샤드의 mongod에 직접 데이터를 넣어주었습니다. 샤딩을 위해 primary key에 인덱스를 생성했고 zipfian 분포로 생성된 하나의 integer 필드에 인덱스를 생성해주었습니다. 이를 통해 좀 더 흥미로운 쿼리를 테스트할 수 있었습니다.
Part1 에서는 저렴한 1PB MongoDB를 구축하기 위한 여러가지 고려사항들에 대해 이야기하고 있습니다. Part1에서 테스트를 위한 모든 준비가 끝났고, 테스트 결과를 위해 Part 2로 넘어갑니다.
Part2
Part2에서는 테스트의 결과에 대해 이야기하고 있습니다.
Result
결론은 다음과 같습니다.
mongos> db.usertable.stats()
{
"objects" : 7170648489,
"avgObjSize" : 147438.99952658816,
"dataSize" : NumberLong("1057240224818640"),
"storageSize" : NumberLong("1057709186089744"),
"numExtents" : 496951,
"indexes" : 432,
"indexSize" : 649064661728,
"fileSize" : NumberLong("1059468228952064"),
"ok" : 1
}
data size는 1,057,240,224,818,640 bytes 입니다.
테스트 결과는 다음과 같습니다.
240개의 SATA 드라이브에서 부하가 진행됨에 따라서 load가 비대칭적으로 되는 것을 보실 수 있습니다. 1PB의 데이터를 저장하는데에는 17 ~ 22시간이 걸렸는데, 이는 몇몇 YCSB 프로세스가 일찍 종료되었기 때문입니다. 초기 YCSB 프로세스는 1분당 50GB를 write 했고, 이는 844MB/s 의 처리량입니다. 그 과정에서 disk 사용량은 50%정도로 유지되었고, CPU 가 병목이 된 것을 확인할 수 있었습니다.
다음은 분당 operation 수입니다.
각 인스턴스 당 AWS는 1.5M IOPM = 25K IOPS = 1K IOPS per drive 를 나타내고 있습니다. 이는 7K RPM SATA 드라이브가 제공할 수 있는 것 보다 많은데, AWS에서는 연속적인 쓰기가 disk에 도달하기 전에 OS 레벨의 작업에서 성공적으로 최적화되고 있는 것 같습니다.
YCSB 프로세스가 local에서 돌고 있다는 것을 감안할 때, 병목은 CPU인 것 같습니다.
약 100$ / hr 정도로, cluster를 오래 살려두지는 않았습니다. YCSB에서 데이터 load에 대한 부분은 원하는 대로 수정했지만, query를 수행하는 부분은 수정하지 못했기 때문에 읽기에 대한 테스트는 비교적 간단한 몇가지 쿼리만 수행했습니다. zipfian 분포로 생성된 인덱스 필드에 쿼리를 수행할 때, 216개의 샤드 전체에 부하를 적절히 분산시키면서 3초 이내로 쿼리가 수행되었습니다.
db.usertable.find({fieldInt0: 17000}, {fieldInt0: 1, fieldInt1: 1}).explain()
...
"cursor" : "BtreeCursor fieldInt0_1",
"n" : 30747,
"nChunkSkips" : 11116,
"nYields" : 0,
"nscanned" : 41863,
"nscannedAllPlans" : 41863,
"nscannedObjects" : 41863,
"nscannedObjectsAllPlans" : 41863,
"millisShardTotal" : 395210,
"millisShardAvg" : 1829,
"numQueries" : 216,
"numShards" : 216,
"millis" : 2702 // 약 2.7초
}
정교한 분산된 쿼리를 수행하기 위해 aggregation framework도 사용해보았습니다.
2번 샤드와 5번 샤드에 데이터가 없는 것은 YCSB의 zipfian generator가 제대로 작동하지 않을 수 있다는 것을 의미합니다.
mongos> db.usertable.aggregate(
... {$match: {fieldInt0: 16000}},
... {$group: {_id: "$fieldInt1", total: {$sum: "$fieldInt2"}}},
... { $sort: { total: -1 } },
... { $limit : 5 }
... )
{
"result" : [
{
"_id" : 0,
"total" : 5160596
},
{
"_id" : 1,
"total" : 2367352
},
{
"_id" : 3,
"total" : 2243450
},
{
"_id" : 4,
"total" : 1402324
},
{
"_id" : 6,
"total" : 1061036
}
],
"ok" : 1
}
1 PB의 데이터에 BenchRun을 수행한 결과로는 FindOne() 연산에 대해 800QPS 정도를 보였습니다.
결과적으로 1PB를 저장하는 비용에서 중요한 것은 바로 스토리지입니다.
hs1 임시 스토리지로 1PB를 돌리기위해서는 시간당 100달러가 조금 넘게 들어갑니다. 이마저도, 인스턴스를 종료하면 바로 사라집니다.
아무튼 MongoDB를 통해 1PB를 저장하고, 사용해보았으며
스토리지의 종류와 document의 block size 등을 다르게 하면 다른 결과가 나올 수 있을 것 같습니다.
'Database > MongoDB' 카테고리의 다른 글
MongoDB의 Failover (0) | 2023.11.19 |
---|---|
MongoDB 로그 관리 : logRotate와 로그파일 권한 (1) | 2023.11.16 |
[MongoDB] MongoDB 빌드하기 (2) | 2023.03.05 |
[MongoDB] 도큐먼트의 배열 필드내 요소 범위를 검사하는 쿼리 (0) | 2023.02.04 |
mongoexport, mongoimport 사용해보기 (0) | 2022.08.05 |