-
Introdução
Este é um post em duas partes no qual compartilharei meus resultados pessoais após realizar uma série de testes de benchmarking do MongoDB 3.6 ao 8.0.
A ideia de dividir os artigos em partes diferentes foi para tornar a leitura mais dinâmica. Nesta primeira parte, explicarei mais sobre os motivos e como o processo foi feito, enquanto a Parte 2 se concentrará nos resultados em si.
Se você quiser pular para a Parte 2, pode clicar no link abaixo. Caso contrário, pode acompanhar esta primeira parte para mais detalhes tais quais o objetivo, ambiente de teste e como o benchmark foi realizado.
-
Objetivo
O objetivo e a motivação são bem simples, eu diria:
Entender se o desempenho do MongoDB mudou ao longo dos anos(versões).
Isso porque essa dúvida acontece a cada novo grande lançamento; Clientes/Usuários no geral sempre expressam preocupações sobre uma possível regressão de desempenho ao migrar para uma versão mais recente.
Embora não seja muito claro e organizado o modo como essa informação é apresentada, temos outros usuários realizando benchmarks e relatando problemas relacionados à perda de desempenho ao longo das versões:
- MongoDB 4.2 vs 4.0, 3.6, 3.4, and 3.2 Benchmarks
- MongoDB 3.4 vs 4.4 vs 5.0 vs 6.0
- SERVER-51104
- SERVER-36875
- Performance degradation after upgrading to 4.4
Dito isso, este teste de benchmark visa entender essas preocupações, avaliando o desempenho de diferentes versões do MongoDB em um ambiente controlado e único.
Embora este teste não pretenda ser a palavra final sobre o desempenho do MongoDB, ele visa servir como um esforço para organizar e elucidar essa questão em todas as versões testadas.
-
Ambiente de Teste
Servidor de banco de dados com o seguinte perfil de configuração:
- CPU: AMD Ryzen(TM) 7 5800X — 8C/16T
- RAM: 16 GB DDR4 a 3200 MHz (2 x 8 GB).
- Discos: 512GB(SSD) -> Instalação do SO e do Banco de Dados. | 512GB(NVMe) -> dbpath do MongoDB.
- SO: Oracle Linux 8.10.
- Kernel: 4.18.0–553.el8_10.x86_64
Para o servidor cliente, temos o seguinte perfil de configuração:
- CPU: Intel(R) Core(TM) i7–9750H CPU
- RAM: 32 GB DDR4 a 2666 MHz (2 x 16 GB).
- SO: Oracle Linux 8.10.
- Kernel: 4.18.0–553.el8_10.x86_64
-
Topologia
Replica Set de nó único.
Esta topologia foi escolhida porque o foco não está na latência de replicação, controle de fluxo (flow control), consultas scatter-gather, leituras em secundários, ou quaisquer outras funcionalidades que pudessem influenciar o desempenho bruto. Em vez disso, o objetivo é avaliar como o desempenho do MongoDB mudou entre as diferentes versões sem a influência de possíveis features.
-
Configuração
Do lado do SO para o banco de dados, todas as notas de produção e melhores práticas foram aplicadas adequadamente:
- Notas de Produção para Implantações Autogerenciadas
- Lista de verificação de operações para implantações autogerenciadas
- Melhores Práticas do MongoDB
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# Percona Toolkit System Summary Report ###################### Date | 2024-10-06 16:25:22 UTC (local TZ: -03 -0300) Hostname | pc-oraclelinux8 Uptime | 2 days, 12 min, 3 users, load average: 0.00, 0.00, 0.02 System | Gigabyte Technology Co., Ltd.; X570S I AORUS PRO AX; v-CF (Desktop) Platform | Linux Release | Red Hat Enterprise Linux release 8.10 (Ootpa) Kernel | 4.18.0-553.el8_10.x86_64 Architecture | CPU = 64-bit, OS = 64-bit Threading | NPTL 2.28 SELinux | Disabled Virtualized | No virtualization detected # Processor ################################################## Processors | physical = 1, cores = 8, virtual = 16, hyperthreading = yes Speeds | 1x3614.310, 15x3800.000 Models | 16xAMD Ryzen 7 5800X 8-Core Processor Caches | 16x512 KB # Memory ##################################################### Total | 15.5G Free | 1.3G Used | physical = 9.1G, swap allocated = 7.8G, swap used = 3.0M, virtual = 9.1G Shared | 76.1M Buffers | 5.1G Caches | 6.2G Dirty | 140 kB UsedRSS | 10.0G Swappiness | 1 DirtyPolicy | 15, 5 DirtyStatus | 0, 0 Numa Nodes | 1 Numa Policy | default Preferred Node | current Node Size Free CPUs ==== ==== ==== ==== node0 15913 MB 1374 MB 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Locator Size Speed Form Factor Type Type Detail ========= ======== ================= ============= ============= ======================================== DIMM 1 8 GB 3200 MT/s DIMM DDR4 Synchronous Unbuffered (Unregistered) DIMM 1 8 GB 3200 MT/s DIMM DDR4 Synchronous Unbuffered (Unregistered) DIMM 0 {EMPTY} Unknown Unknown Unknown DIMM 0 {EMPTY} Unknown Unknown Unknown # Mounted Filesystems ######################################## Filesystem Size Used Type Opts Mountpoint /dev/mapper/ol_oraclelinux8-home 387G 1% xfs rw,noatime,attr2,inode64,logbufs=8,logbsize=32k,noquota /home /dev/mapper/ol_oraclelinux8-root 70G 14% xfs rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota / /dev/nvme0n1p1 96M 33% vfat rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=ascii,shortname=winnt,errors=remount-ro /boot/efi /dev/nvme0n1p2 1014M 46% xfs rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota /boot /dev/nvme1n1 466G 2% xfs rw,noatime,attr2,inode64,logbufs=8,logbsize=32k,noquota /mnt/nvme-mongodb # Disk Schedulers And Queue Size ############################# dm-0 | 128 dm-1 | 128 dm-2 | 128 nvme0n1 | [mq-deadline] 256 nvme1n1 | [mq-deadline] 256 # Kernel Inode State ######################################### dentry-state | 157146 131332 45 0 76070 0 file-nr | 7488 0 1617863 inode-nr | 80659 633 # Network Config ############################################# Controller | Intel Corporation Ethernet Controller I225-V (rev 01) FIN Timeout | 30 Port Range | 60999 # Simplified and fuzzy rounded vmstat ######################## procs ---swap-- -----io---- ---system---- --------cpu-------- r b si so bi bo ir cs us sy il wa st 1 0 0 0 1 70 1 7 1 0 99 0 0 7 0 0 0 0 0 5000 4500 1 1 98 0 0 0 0 0 0 0 4 3000 3000 1 0 99 0 0 0 0 0 0 0 0 400 500 0 0 100 0 0 0 0 0 0 0 0 300 450 0 0 100 0 0 $ sudo blockdev --getra /dev/mapper/ol_oraclelinux8-home 16 $ sudo blockdev --getra /dev/nvme1n1 16 $ cat /proc/$(pgrep mongod)/limits Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 8388608 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes 64000 64000 processes Max open files 64000 64000 files Max locked memory unlimited unlimited bytes Max address space unlimited unlimited bytes Max file locks unlimited unlimited locks Max pending signals 127401 127401 signals Max msgqueue size 819200 819200 bytes Max nice priority 0 0 Max realtime priority 0 0 Max realtime timeout unlimited unlimited us |
Do lado do Banco de Dados, a instância está rodando com parâmetros padrão, com apenas Replica Set e Authorization (autorização) habilitados:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
rs0:PRIMARY> db.adminCommand({ getCmdLineOpts: 1 }) { "argv" : [ "/usr/bin/mongod", "-f", "/etc/mongod.conf" ], "parsed" : { "config" : "/etc/mongod.conf", "net" : { "bindIp" : "127.0.0.1, 192.168.15.50", "port" : 27017 }, "processManagement" : { "fork" : true, "pidFilePath" : "/var/run/mongodb/mongod.pid", "timeZoneInfo" : "/usr/share/zoneinfo" }, "replication" : { "replSetName" : "rs0" }, "security" : { "authorization" : "enabled", "keyFile" : "/mnt/nvme-mongodb/keys/keyfile" }, "storage" : { "dbPath" : "/mnt/nvme-mongodb/data", "journal" : { "enabled" : true } }, "systemLog" : { "destination" : "file", "logAppend" : true, "path" : "/var/log/mongodb/mongod.log" } } |
-
Ferramenta de Benchmarking
A ferramenta utilizada para este teste foi o mongo-perf.
- Versão do Driver — PyMongo 4.4.0.
- Versão do Python — Python3.8.
Conforme a documentação de compatibilidade, PyMongo 4.4.0 e Python3.8 fornecem toda a compatibilidade necessária do 3.6 ao 8.0.
A ferramenta mongo-perf utiliza scripts Python para conduzir execuções de testes específicos que avaliam vários aspectos de desempenho. Atualmente, cerca de 34 scripts estão disponíveis para uso, mas eu não usei todos eles.
Para os testes, os seguintes scripts foram selecionados:
- simple_insert.js
- simple_update.js
- simple_query.js
- simple_remove.js
- complex_insert.js
- complex_update.js
- partial_index.js
A escolha desses scripts selecionados se deve ao fato de que eles podem testar de forma abrangente operações de CRUD, na qual maior parte dos ambientes recebem.
Os scripts simple_insert.js, simple_update.js, simple_query.js e simple_remove.js cobrem o básico de criação, modificação, recuperação e exclusão de documentos. O complex_update.js adiciona complexidade ao envolver operações avançadas e múltiplos índices. O complex_insert.js e o partial_index.js avaliam o desempenho em cenários mais exigentes, como inserções sequenciais e aleatórias, com e sem contenção, envolvendo valores grandes e índices multi-key.
-
Metodologia e Consideração Final
Este teste foi conduzido na época do lançamento do MongoDB 8.0, e os últimos patch releases disponíveis das outras versões foram usados, de acordo:
- 3.6.23 -> 4.0.28 -> 4.2.25 -> 4.4.29 -> 5.0.29 -> 6.0.18 -> 7.0.14 -> 8.0.0.
O Red Hat 8 foi escolhido como sistema exclusivamente devido à sua alta compatibilidade entre as versões testadas. Mais adiante nesta seção, eu detalho melhor os testes, mas abaixo, temos um diagrama mostrando como as versões do MongoDB foram testadas.
- Cada versão foi testada seguindo a abordagem de upgrade de patch:

Abaixo, mais detalhes de como o processo de benchmark ocorreu:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#!/usr/bin/bash tests=("simple_insert" "simple_remove" "simple_update" "complex_insert" "complex_update" "partial_index") threads=(1 2 4 8 16) # Run the main set of tests for t in "${tests[@]}"; do for th in "${threads[@]}"; do for i in {1..10}; do echo "==== Test $t with $th threads, Round $i ====" python3.8 -u benchrun.py -f testcases/$t.js -- host 192.168.15.70 -- port 27017 -- replset rs0 -u admin -p sekret -t $th -- trialTime 1 -- out /home/jean/benchtools/results/8.0-$t-$th-$i.json -- tsvSummary true -- writeCmd true -- readCmd true -w 1 done done done # Run the separate test with simple_query.js and -- includeFilter core echo "==== Running simple_query.js with core filter ====" for th in "${threads[@]}"; do for i in {5..10}; do echo "==== Test simple_query with $th threads, Round $i ====" python3.8 -u benchrun.py -f testcases/simple_query.js -- host 192.168.15.70 -- port 27017 -- replset rs0 -u admin -p sekret -t $th -- trialTime 1 -- out /home/jean/benchtools/results/8.0-simple_query-$t-$th-$i.json -- tsvSummary true -- writeCmd true -- readCmd true -w 1 -- includeFilter core done done |
É um shell script que interage com os testcases da ferramenta mongo-perf como listado anteriormente; Da ferramenta de benchmark, vale a pena mencionar as opções utilizadas:
-t $th:
- Define o número de threads a serem usadas no benchmark.
- A variável $th é substituída dinamicamente por valores no loop (ex: 1, 2, 4, 8, 16).
-trialTime 1:
- Define a duração de cada tentativa em segundos; No teste, cada tentativa dura 1 segundo.
-writeCmd true:
- Habilita o uso de write commands (comandos de escrita) (ex: insert, update, delete) em vez de operações de escrita legadas.
- O MongoDB introduziu os write commands em versões mais recentes para operações de escrita mais eficientes.
-readCmd true:
- Habilita o uso de read commands (comandos de leitura) (ex: find, getMore) em vez de operações de leitura legadas.
-w 1:
- Especifica o nível de write concern.
Com relação o script, cada teste de CRUD foi executado dez vezes para o número de threads fornecido (1, 2, 4, 8, 16) pela duração de um segundo.
- Por exemplo: O simple_insert.js é um script que testa diferentes operações de inserção, cada uma dessas operações foi executada por 1 segundo, 10 vezes usando 1 thread. Após terminar essa sequência de operações, o loop reinicia, mas desta vez as operações para o simple_insert.js serão testadas com 2 threads, depois 4 e assim por diante até esgotar a variável de threads.
Após executar todos os scripts CRUD mencionados para uma versão, uma limpeza de dados foi realizada. Para cada operação testada, teremos 10 saídas distintas. Os outliers (o mais rápido e o mais lento) foram removidos, e as outras 8 operações restantes são somadas e divididas, gerando uma média de valores para aquela operação.
- Por exemplo: O simple_insert.js tem a operação Insert.EmptyCapped, que basicamente testa a inserção de um documento vazio em uma capped collection. Temos então 10 tempos de execução distintos rodando com 1 thread: 547.96, 552.54, 561.64, 562.18, 562.73, 564.83, 567.14, 570.20, 542.68, 567.62. Os outliers seriam o número mais alto = 570.20 e o número mais baixo = 542.68; A média de operações por segundo seria 560,83.
Este cálculo foi aplicado a todas as operações para todas as threads e foi organizado em tabelas, que você pode ver e seguir na parte 2, onde temos o relatório finalizado.
Até mais!
