Nesta postagem, comparamos dois dos bancos de dados NoSQL mais populares:Redis (na memória) e MongoDB (mecanismo de armazenamento de memória Percona).
O Redis é um armazenamento de estrutura de banco de dados em memória popular e muito rápido usado principalmente como cache ou agente de mensagens. Sendo na memória, é o armazenamento de dados preferido quando os tempos de resposta superam todo o resto.
MongoDB é um armazenamento de documentos em disco que fornece uma interface JSON para dados e possui uma linguagem de consulta muito rica. Conhecido por sua velocidade, eficiência e escalabilidade, atualmente é o banco de dados NoSQL mais popular usado atualmente. No entanto, sendo um banco de dados em disco, não pode ser comparado favoravelmente a um banco de dados em memória como o Redis em termos de desempenho absoluto. Mas, com a disponibilidade dos mecanismos de armazenamento em memória para o MongoDB, uma comparação mais direta se torna viável.
Percona Memory Engine para MongoDB
A partir da versão 3.0, o MongoDB fornece uma API para conectar o mecanismo de armazenamento de sua escolha. Um mecanismo de armazenamento, do contexto do MongoDB, é o componente do banco de dados responsável por gerenciar como os dados são armazenados, tanto na memória quanto no disco. O MongoDB suporta um mecanismo de armazenamento na memória, no entanto, atualmente está limitado à edição Enterprise do produto. Em 2016, a Percona lançou um mecanismo na memória de código aberto para o MongoDB Community Edition chamado Percona Memory Engine para MongoDB. Assim como o mecanismo de memória do MonogDB, também é uma variação do mecanismo de armazenamento WiredTiger, mas sem persistência no disco.
Com um mecanismo de armazenamento MongoDB na memória, temos condições equitativas entre o Redis e o MongoDB. Então, por que precisamos comparar os dois? Vejamos as vantagens de cada um deles como uma solução de cache.
Vamos dar uma olhada no Redis primeiro.
Vantagens do Redis como cache
- Uma solução de cache bem conhecida que se destaca.
- O Redis não é uma solução de cache simples. Ele tem estruturas de dados avançadas que fornecem muitas maneiras poderosas de salvar e consultar dados que não podem ser alcançados com um cache de valor-chave vanilla.
- O Redis é bastante simples de configurar, usar e aprender.
- O Redis oferece persistência que você pode configurar. Portanto, o aquecimento do cache em caso de falha é fácil.
Desvantagens do Redis:
- Ele não tem criptografia integrada na rede.
- Sem controle de conta baseado em função (RBAC).
- Não existe uma solução de clusterização perfeita e madura.
- Pode ser difícil implantar em implantações de nuvem em grande escala.
Vantagens do MongoDB como Cache
- MongoDB é um banco de dados mais tradicional com recursos avançados de manipulação de dados (pense em agregações e redução de mapa) e uma linguagem de consulta avançada.
- SSL, RBAC e expansão integrada.
- Se você já estiver usando o MongoDB como banco de dados principal, seus custos operacionais e de desenvolvimento cairão porque você só terá um banco de dados para aprender e gerenciar.
Veja este post de Peter Zaitsev sobre onde o mecanismo de memória MongoDB pode ser uma boa opção.
Desvantagem do MongoDB:
- Com um mecanismo na memória, ele não oferece persistência até que seja implantado como um conjunto de réplicas com persistência configurada nas réplicas de leitura.
Nesta postagem, vamos nos concentrar em quantificar as diferenças de desempenho entre Redis e MongoDB . Uma comparação qualitativa e diferenças operacionais serão abordadas em cargos subsequentes.
Redis x MongoDB na memória
Desempenho
- O Redis tem um desempenho consideravelmente melhor para leituras para todos os tipos de cargas de trabalho e melhor para gravações à medida que as cargas de trabalho aumentam.
- Mesmo que o MongoDB utilize todos os núcleos do sistema, ele é vinculado à CPU comparativamente mais cedo. Embora ainda tivesse computação disponível, era melhor em gravações do que o Redis.
- Ambos os bancos de dados são eventualmente vinculados à computação. Embora o Redis seja single-thread, ele (principalmente) faz mais com a execução em um núcleo do que o MongoDB enquanto satura todos os núcleos.
- Redis , para conjuntos de dados não triviais, usa muito mais RAM em comparação com o MongoDB para armazenar a mesma quantidade de dados.
Configuração
Usamos o YCSB para medir o desempenho e o usamos para comparar e comparar o desempenho do MongoDB em vários provedores de nuvem e configurações no passado. Assumimos um entendimento básico das cargas de trabalho e recursos do YCSB na descrição do equipamento de teste.
- Tipo de instância do banco de dados: AWS EC2 c4.xlarge com 4 núcleos, 7,5 GB de memória e rede aprimorada para garantir que não tenhamos gargalos de rede.
- Máquina do cliente: AWS EC2 c4.xlarge na mesma nuvem privada virtual (VPC) que os servidores de banco de dados.
- Redis: Versão 3.2.8 com AOF e RDB desativados. Independente.
- MongoDB: Percona Memory Engine baseado no MongoDB versão 3.2.12. Independente.
- Rendimento da rede : Medido via iperf conforme recomendado pela AWS:
Test Complete. Summary Results: [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-60.00 sec 8.99 GBytes 1.29 Gbits/sec 146 sender [ 4] 0.00-60.00 sec 8.99 GBytes 1.29 Gbits/sec receiver
- Detalhes da carga de trabalho
- Inserir carga de trabalho: 100% Write – 2,5 milhões de registros
- Carga de trabalho A: Atualizar carga de trabalho pesada – 50%/50% de leituras/gravações – 25 milhões de operações
- Carga de trabalho B: Leia principalmente carga de trabalho – 95%/5% de leituras/gravações – 25 milhões de operações
- Carregamento do cliente: Taxa de transferência e latência medidas em cargas cada vez maiores geradas a partir do cliente. Isso foi feito aumentando o número de threads de carregamento do cliente YCSB, começando em 8 e crescendo em múltiplos de 2
Resultados
Desempenho da carga de trabalho B
Como o principal caso de uso para bancos de dados na memória é o cache, vamos analisar primeiro a carga de trabalho B.
Aqui estão os números de taxa de transferência/latência da carga de trabalho de 25 milhões de operações e a proporção de leituras/gravações foi de 95%/5%. Esta seria uma carga de trabalho de leitura de cache representativa:
Observação:a taxa de transferência é plotada em relação ao eixo primário (esquerda), enquanto a latência é plotada em relação ao eixo secundário (direita).
Observações durante a execução da carga de trabalho B:
- Para o MongoDB, a CPU foi saturada por 32 threads em diante. Uso superior a 300% com porcentagens ociosas de um dígito.
- Para Redis, a utilização da CPU nunca ultrapassou 95%. Assim, o Redis teve um desempenho consistente melhor do que o MongoDB ao ser executado em um único thread, enquanto o MongoDB estava saturando todos os núcleos da máquina.
- Para Redis, com 128 threads, as execuções falham com frequência com exceções de tempo limite de leitura.
Desempenho da carga de trabalho A
Aqui estão os números de taxa de transferência/latência da carga de trabalho de 25 milhões de operações. A proporção de leituras/gravações foi de 50%/50%:
Observação:a taxa de transferência é plotada em relação ao eixo primário (esquerda), enquanto a latência é plotada em relação ao eixo secundário (direita).
Observações durante a execução da carga de trabalho A:
- Para o MongoDB, a CPU foi saturada por 32 threads em diante. Uso superior a 300% com porcentagens ociosas de um dígito.
- Para Redis, a utilização da CPU nunca ultrapassou 95%.
- Para Redis, com 64 threads e acima, as execuções falham frequentemente com exceções de tempo limite de leitura.
Inserir desempenho da carga de trabalho
Por fim, aqui estão os números de taxa de transferência/latência da carga de trabalho de inserção de 2,5 milhões de registros. O número de registros foi selecionado para garantir que a memória total fosse usada no evento Redis que não excedesse 80% (já que o Redis é o consumidor de memória, consulte o Apêndice B).
Observação:a taxa de transferência é plotada em relação ao eixo primário (esquerda), enquanto a latência é plotada em relação ao eixo secundário (direita).
Observações durante a execução da carga de trabalho de inserção:
- Para o MongoDB, a CPU foi saturada por 32 threads em diante. Uso superior a 300% com porcentagens ociosas de um dígito.
- Para Redis, a utilização da CPU nunca ultrapassou 95%.
Apêndices
A:Desempenho de thread único
Eu tive um forte desejo de descobrir isso – mesmo que não seja muito útil em condições do mundo real:quem seria melhor ao aplicar a mesma carga a cada um deles a partir de um único thread. Ou seja, qual seria o desempenho de um aplicativo de thread único?
B:Tamanho do banco de dados
O formato padrão dos registros inseridos pelo YCSB são:cada registro é de 10 campos e cada campo é de 100 bytes. Supondo que cada registro tenha cerca de 1 KB, o tamanho total esperado na memória seria superior a 2,4 GB. Houve um grande contraste nos tamanhos reais, conforme visto nos bancos de dados.
MongoDB
> db.usertable.count() 2500000 > db.usertable.findOne() { "_id" : "user6284781860667377211", "field1" : BinData(0,"OUlxLllnPC0sJEovLTpyL18jNjk6ME8vKzF4Kzt2OUEzMSEwMkBvPytyODZ4Plk7KzRmK0FzOiYoNFU1O185KFB/IVF7LykmPkE9NF1pLDFoNih0KiIwJU89K0ElMSAgKCF+Lg=="), "field0" : BinData(0,"ODlwIzg0Ll5vK1s7NUV1O0htOVRnMk53JEd3KiwuOFN7Mj5oJ1FpM11nJ1hjK0BvOExhK1Y/LjEiJDByM14zPDtgPlcpKVYzI1kvKEc5PyY6OFs9PUMpLEltNEI/OUgzIFcnNQ=="), "field7" : BinData(0,"N155M1ZxPSh4O1B7IFUzJFNzNEB1OiAsM0J/NiMoIj9sP1Y1Kz9mKkJ/OiQsMSk2OCouKU1jOltrMj4iKEUzNCVqIV4lJC0qIFY3MUo9MFQrLUJrITdqOjJ6NVs9LVcjNExxIg=="), "field6" : BinData(0,"Njw6JVQnMyVmOiZyPFxrPz08IU1vO1JpIyZ0I1txPC9uN155Ij5iPi5oJSIsKVFhP0JxM1svMkphL0VlNzdsOlQxKUQnJF4xPkk9PUczNiF8MzdkNy9sLjg6NCNwIy1sKTw6MA=="), "field9" : BinData(0,"KDRqP1o3KzwgNUlzPjwgJEgtJC44PUUlPkknKU5pLzkuLEAtIlg9JFwpKzBqIzo2MCIoKTxgNU9tIz84OFB/MzJ4PjwoPCYyNj9mOjY+KU09JUk1I0l9O0s/IEUhNU05NShiNg=="), "field8" : BinData(0,"NDFiOj9mJyY6KTskO0A/OVg/NkchKEFtJUprIlJrPjYsKT98JyI8KFwzOEE7ICR4LUF9JkU1KyRkKikoK0g3MEMxKChsL10pKkAvPFRxLkxhOlotJFZlM0N/LiR4PjlqJ0FtOw=="), "field3" : BinData(0,"OSYoJTR+JEp9K00pKj0iITVuIzVqPkBpJFN9Myk4PDhqOjVuP1YhPSM2MFp/Kz14PTF4Mlk3PkhzKlx3L0xtKjkqPCY4JF0vIic6LEx7PVBzI0U9KEM1KDV4NiEuKFx5MiZyPw=="), "field2" : BinData(0,"Njd8LywkPlg9IFl7KlE5LV83ISskPVQpNDYgMEprOkprMy06LlotMUF5LDZ0IldzLl0tJVkjMTdgJkNxITFsNismLDxuIyYoNDgsLTc+OVpzKkBlMDtoLyBgLlctLCxsKzl+Mw=="), "field5" : BinData(0,"OCJiNlI1O0djK1BtIyc4LEQzNj9wPyQiPT8iNE1pODI2LShqNDg4JF1jNiZiNjZuNE5lNzA8OCAgMDp2OVkjNVU3MzIuJTgkNDp0IyVkJyk6IEEvKzVyK1s9PEAhKUJvPDxyOw=="), "field4" : BinData(0,"OFN1I0B7N1knNSR2LFp7PjUyPlJjP15jIUdlN0AhNEkhMC9+Lkd5P10jO1B3K10/I0orIUI1NzYuME81I0Y1NSYkMCxyI0w/LTc8PCEgJUZvMiQiIkIhPCF4LyN6K14rIUJlJg==") } > db.runCommand({ dbStats: 1, scale: 1 }) { "db" : "ycsb", "collections" : 1, "objects" : 2500000, "avgObjSize" : 1167.8795252, "dataSize" : 2919698813, "storageSize" : 2919698813, "numExtents" : 0, "indexes" : 1, "indexSize" : 76717901, "ok" : 1 }
Portanto, o espaço ocupado é de ~ 2,7 GB que é bem próximo do que esperávamos.
Redis
Vamos olhar para o Redis agora.
> info keyspace # Keyspace db0:keys=2500001,expires=0,avg_ttl=0 127.0.0.1:6379> RANDOMKEY "user3176318471616059981" 127.0.0.1:6379> hgetall user3176318471616059981 1) "field1" 2) "#K/<No\"&l*M{,;f;]\x7f)Ss'+2<D}7^a8I/01&9.:)Q71T7,3r&\\y6:< Gk;6n*]-)*f>:p:O=?<:(;v/)0)Yw.W!8]+4B=8.z+*4!" 3) "field2" 4) "(9<9P5**d7<v((2-6*3Zg/.p4G=4Us;N+!C! I50>h=>p\"X9:Qo#C9:;z.Xs=Wy*H3/Fe&0`8)t.Ku0Q3)E#;Sy*C).Sg++t4@7-" 5) "field5" 6) "#1 %8x='l?5d38~&U!+/b./b;(6-:v!5h.Ou2R}./(*)4!8>\"B'!I)5U?0\" >Ro.Ru=849Im+Qm/Ai(;:$Z',]q:($%&(=3~5(~?" 7) "field0" 8) "+\"(1Pw.>*=807Jc?Y-5Nq#Aw=%*57r7!*=Tm!<j6%t3-45L5%Cs#/h;Mg:Vo690-/>-X}/X#.U) )f9-~;?p4;p*$< D-1_s!0p>" 9) "field7" 10) ":]o/2p/3&(!b> |#:0>#0-9b>Pe6[}<Z{:S}9Uc*0<)?60]37'~'Jk-Li',x!;.5H'\"'|.!v4Y-!Hk=E\x7f2;8*9((-09*b#)x!Pg2" 11) "field3" 12) " C; ,f6Uq+^i Fi'8&0By\"^##Qg\":$+7$%Y;7Rs'\"d3Km'Es>.|33$ Vo*M%=\"<$&j%/<5]%\".h&Kc'5.46x5D35'0-3l:\"| !l;" 13) "field6" 14) "-5x6!22)j;O=?1&!:&.S=$;|//r'?d!W54(j!$:-H5.*n&Zc!0f;Vu2Cc?E{1)r?M'!Kg'-b<Dc*1d2M-9*d&(l?Uk5=8,>0.B#1" 15) "field9" 16) "(Xa&1t&Xq\"$((Ra/Q9&\": &>4Ua;Q=!T;(Vi2G+)Uu.+|:Ne;Ry3U\x7f!B\x7f>O7!Dc;V7?Eu7E9\"&<-Vi>7\"$Q%%A%1<2/V11: :^c+" 17) "field8" 18) "78(8L9.H#5N+.E5=2`<Wk+Pw?+j'Q=3\"$,Nk3O{+3p4K?0/ 5/r:W)5X}#;p1@\x7f\"+&#Ju+Z97#t:J9$'*(K).7&0/` 125O38O)0" 19) "field4" 20) "$F=)Ke5V15_)-'>=C-/Ka7<$;6r#_u F9)G/?;t& x?D%=Ba Zk+]) ($=I%3P3$<`>?*=*r9M1-Ye:S%%0,(Ns3,0'A\x7f&Y12A/5" 127.0.0.1:6379> info memory # Memory used_memory:6137961456 used_memory_human:5.72G used_memory_rss:6275940352 used_memory_rss_human:5.84G used_memory_peak:6145349904 used_memory_peak_human:5.72G total_system_memory:7844429824 total_system_memory_human:7.31G used_memory_lua:37888 used_memory_lua_human:37.00K maxmemory:7516192768 maxmemory_human:7.00G maxmemory_policy:noeviction mem_fragmentation_ratio:1.02 mem_allocator:jemalloc-3.6.0
No pico de uso, o Redis parece estar consumindo cerca de 5,72 G de memória, ou seja, duas vezes mais memória do que o MongoDB. Agora, essa comparação pode não ser perfeita devido às diferenças nos dois bancos de dados, mas essa diferença no uso de memória é muito grande para ser ignorada. YCSB insere registro em um hash no Redis e um índice é mantido em um conjunto classificado. Como uma entrada individual é maior que 64, o hash é codificado normalmente e não há economia de espaço. O desempenho do Redis tem o preço do aumento do consumo de memória.
Isso, em nossa opinião, pode ser um ponto de dados importante na escolha entre MongoDB e Redis. O MongoDB pode ser preferível para usuários que se preocupam em reduzir seus custos de memória.
C:Taxa de transferência da rede
Um servidor de banco de dados na memória pode ser vinculado a computação ou E/S de rede, portanto, era importante durante todo o conjunto desses testes garantir que nunca estivéssemos vinculados à rede. Medir a taxa de transferência da rede durante a execução de testes de taxa de transferência do aplicativo afeta negativamente a medição geral da taxa de transferência. Então, executamos medições subsequentes de taxa de transferência de rede usando iftop nas contagens de encadeamentos em que as maiores taxas de transferência de gravação foram observadas. Descobriu-se que isso era de cerca de 440 Mbps para Redis e MongoDB em suas respectivas taxas de transferência de pico. Dada a nossa medição inicial da largura de banda máxima da rede em torno de 1,29 Gbps, temos certeza de que nunca atingiremos os limites da rede. Na verdade, ele apenas suporta a inferência de que, se o Redis fosse multi-core, poderíamos obter números muito melhores.