Dans cet article, nous comparons deux des bases de données NoSQL les plus populaires :Redis (en mémoire) et MongoDB (moteur de stockage en mémoire Percona).
Redis est un magasin de structure de base de données en mémoire populaire et très rapide principalement utilisé comme cache ou courtier de messages. Étant en mémoire, c'est le magasin de données de choix lorsque les temps de réponse l'emportent sur tout le reste.
MongoDB est un magasin de documents sur disque qui fournit une interface JSON aux données et dispose d'un langage de requête très riche. Connue pour sa vitesse, son efficacité et son évolutivité, c'est actuellement la base de données NoSQL la plus utilisée aujourd'hui. Cependant, étant une base de données sur disque, elle ne peut pas se comparer favorablement à une base de données en mémoire comme Redis en termes de performances absolues. Mais, avec la disponibilité des moteurs de stockage en mémoire pour MongoDB, une comparaison plus directe devient possible.
Moteur de mémoire Percona pour MongoDB
À partir de la version 3.0, MongoDB fournit une API pour brancher le moteur de stockage de votre choix. Un moteur de stockage, du contexte MongoDB, est le composant de la base de données chargé de gérer la manière dont les données sont stockées, à la fois en mémoire et sur disque. MongoDB prend en charge un moteur de stockage en mémoire, cependant, il est actuellement limité à l'édition Enterprise du produit. En 2016, Percona a publié un moteur en mémoire open source pour l'édition communautaire de MongoDB appelé le moteur de mémoire Percona pour MongoDB. Comme le moteur en mémoire de MonogDB, il s'agit également d'une variante du moteur de stockage WiredTiger, mais sans persistance sur disque.
Avec un moteur de stockage en mémoire MongoDB en place, nous avons des règles du jeu équitables entre Redis et MongoDB. Alors, pourquoi devons-nous comparer les deux? Examinons les avantages de chacun d'eux en tant que solution de mise en cache.
Regardons d'abord Redis.
Avantages de Redis en tant que cache
- Une solution de mise en cache bien connue qui excelle dans ce domaine.
- Redis n'est pas une simple solution de cache : il possède des structures de données avancées qui offrent de nombreux moyens puissants d'enregistrer et d'interroger des données qui ne peuvent pas être obtenus avec un cache clé-valeur vanille.
- Redis est assez simple à configurer, à utiliser et à apprendre.
- Redis fournit une persistance que vous pouvez choisir de configurer, de sorte que le préchauffage du cache en cas de plantage est sans tracas.
Inconvénients de Redis :
- Il n'y a pas de chiffrement intégré sur le câble.
- Pas de contrôle de compte basé sur les rôles (RBAC).
- Il n'existe pas de solution de clustering homogène et mature.
- Peut être fastidieux à déployer dans des déploiements cloud à grande échelle.
Avantages de MongoDB en tant que cache
- MongoDB est une base de données plus traditionnelle avec des fonctionnalités avancées de manipulation de données (pensez aux agrégations et à la réduction de carte) et un langage de requête riche.
- SSL, RBAC et scale-out intégrés.
- Si vous utilisez déjà MongoDB comme base de données principale, vos coûts d'exploitation et de développement diminuent, car vous n'avez qu'une seule base de données à apprendre et à gérer.
Regardez ce post de Peter Zaitsev sur où le moteur en mémoire MongoDB pourrait être un bon choix.
Inconvénient de MongoDB :
- Avec un moteur en mémoire, il n'offre aucune persistance jusqu'à ce qu'il soit déployé en tant qu'ensemble de réplicas avec la persistance configurée sur le ou les réplicas en lecture.
Dans cet article, nous nous concentrerons sur la quantification des différences de performances entre Redis et MongoDB . Une comparaison qualitative et les différences opérationnelles seront abordées dans les articles suivants.
Redis contre MongoDB en mémoire
Performances
- Redis fonctionne considérablement mieux pour les lectures pour toutes sortes de charges de travail, et meilleur pour les écritures à mesure que les charges de travail augmentent.
- Même si MongoDB utilise tous les cœurs du système, il est lié au processeur relativement tôt. Même s'il avait encore du calcul disponible, il était meilleur en écriture que Redis.
- Les deux bases de données sont finalement liées au calcul. Même si Redis est monothread, il est (principalement) plus efficace en s'exécutant sur un cœur que MongoDB en saturant tous les cœurs.
- Redis , pour les ensembles de données non triviaux, utilise beaucoup plus de RAM que MongoDB pour stocker la même quantité de données.
Configuration
Nous avons utilisé YCSB pour mesurer les performances, et nous l'avons utilisé pour comparer et évaluer les performances de MongoDB sur divers fournisseurs de cloud et configurations dans le passé. Nous supposons une compréhension de base des charges de travail et des fonctionnalités YCSB dans la description du banc d'essai.
- Type d'instance de base de données : AWS EC2 c4.xlarge avec 4 cœurs, 7,5 Go de mémoire et une mise en réseau améliorée pour garantir l'absence de goulots d'étranglement sur le réseau.
- Ordinateur client : AWS EC2 c4.xlarge dans le même cloud privé virtuel (VPC) que les serveurs de base de données.
- Redis : Version 3.2.8 avec AOF et RDB désactivés. Autonome.
- MongoDB : Moteur de mémoire Percona basé sur MongoDB version 3.2.12. Autonome.
- Débit réseau : Mesuré via iperf comme recommandé par 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
- Détails de la charge de travail
- Insérez la charge de travail : 100 % d'écriture - 2,5 millions d'enregistrements
- Charge de travail A : Mettre à jour une charge de travail importante – 50 %/50 % de lectures/écritures – 25 millions d'opérations
- Charge de travail B : Charge de travail principalement en lecture :95 %/5 % de lectures/écritures :25 millions d'opérations
- Charge client : Débit et latence mesurés sur des charges croissantes générées par le client. Cela a été fait en augmentant le nombre de threads de chargement client YCSB, commençant à 8 et augmentant par multiples de 2
Résultats
Performances de la charge de travail B
Étant donné que le principal cas d'utilisation des bases de données en mémoire est le cache, examinons d'abord la charge de travail B.
Voici les chiffres de débit/latence de la charge de travail de 25 millions d'opérations et le rapport lectures/écritures était de 95 %/5 %. Il s'agirait d'une charge de travail de lecture de cache représentative :
Remarque :le débit est tracé par rapport à l'axe principal (à gauche), tandis que la latence est tracée par rapport à l'axe secondaire (à droite).
Observations pendant l'exécution de la charge de travail B :
- Pour MongoDB, le processeur était saturé à partir de 32 threads. Utilisation supérieure à 300 % avec des pourcentages d'inactivité à un chiffre.
- Pour Redis, l'utilisation du processeur n'a jamais dépassé les 95 %. Ainsi, Redis fonctionnait systématiquement mieux que MongoDB lorsqu'il s'exécutait sur un seul thread, tandis que MongoDB saturait tous les cœurs de la machine.
- Pour Redis, à 128 threads, les exécutions échouaient souvent avec des exceptions de délai de lecture.
Charger de travail une performance
Voici les chiffres de débit/latence de la charge de travail de 25 millions d'opérations. Le ratio de lectures/écritures était de 50 %/50 % :
Remarque :Le débit est tracé par rapport à l'axe principal (à gauche), tandis que la latence est tracée par rapport à l'axe secondaire (à droite).
Observations pendant l'exécution de la charge de travail A :
- Pour MongoDB, le processeur était saturé à partir de 32 threads. Utilisation supérieure à 300 % avec des pourcentages d'inactivité à un chiffre.
- Pour Redis, l'utilisation du processeur n'a jamais dépassé 95 %.
- Pour Redis, avec 64 threads et plus, les exécutions échouaient souvent avec des exceptions de délai de lecture.
Insérer les performances de la charge de travail
Enfin, voici les chiffres de débit/latence de la charge de travail d'insertion de 2,5 millions d'enregistrements. Le nombre d'enregistrements a été sélectionné pour s'assurer que la mémoire totale a été utilisée dans l'événement Redis qui n'a pas dépassé 80 % (puisque Redis est le porc de la mémoire, voir l'annexe B).
Remarque :Le débit est tracé par rapport à l'axe principal (à gauche), tandis que la latence est tracée par rapport à l'axe secondaire (à droite).
Observations lors de l'exécution de l'insertion de la charge de travail :
- Pour MongoDB, le processeur était saturé à partir de 32 threads. Utilisation supérieure à 300 % avec des pourcentages d'inactivité à un chiffre.
- Pour Redis, l'utilisation du processeur n'a jamais dépassé 95 %.
Annexes
A :Performances à un seul thread
J'avais une forte envie de le découvrir - même si ce n'est pas très utile dans des conditions réelles :qui serait mieux en appliquant la même charge à chacun d'eux à partir d'un seul thread. C'est-à-dire, comment une application mono-thread fonctionnerait-elle ?
B :Taille de la base de données
Le format par défaut des enregistrements insérés par YCSB est :chaque enregistrement est de 10 champs et chaque champ est de 100 octets. En supposant que chaque enregistrement soit d'environ 1 Ko, la taille totale attendue en mémoire serait supérieure à 2,4 Go. Il y avait un contraste frappant dans les tailles réelles comme on le voit dans les bases de données.
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 }
Ainsi, l'espace occupé est d'environ 2,7 Go, ce qui est assez proche de ce à quoi nous nous attendions.
Redis
Regardons Redis maintenant.
> 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
Au pic d'utilisation, Redis semble utiliser environ 5,72 Go de mémoire, soit deux fois plus de mémoire que MongoDB. Maintenant, cette comparaison n'est peut-être pas parfaite en raison des différences entre les deux bases de données, mais cette différence d'utilisation de la mémoire est trop importante pour être ignorée. YCSB insère un enregistrement dans un hachage dans Redis et un index est conservé dans un ensemble trié. Étant donné qu'une entrée individuelle est supérieure à 64, le hachage est encodé normalement et il n'y a pas d'économie d'espace. Les performances de Redis se font au prix d'une empreinte mémoire accrue.
Cela, à notre avis, peut être un point de données important dans le choix entre MongoDB et Redis :MongoDB pourrait être préférable pour les utilisateurs soucieux de réduire leurs coûts de mémoire.
C :Débit réseau
Un serveur de base de données en mémoire est susceptible d'être soit lié au calcul, soit lié aux E/S réseau, il était donc important tout au long de ces tests de s'assurer que nous n'étions jamais liés au réseau. La mesure du débit du réseau lors de l'exécution de tests de débit d'application affecte négativement la mesure du débit global. Nous avons donc effectué des mesures de débit réseau ultérieures à l'aide d'iftop au nombre de threads où les débits d'écriture les plus élevés ont été observés. Cela s'est avéré être d'environ 440 Mbps pour Redis et MongoDB à leur débit de pointe respectif. Étant donné que notre mesure initiale de la bande passante maximale du réseau était d'environ 1,29 Gbps, nous sommes certains que nous n'avons jamais atteint les limites du réseau. En fait, cela ne prend en charge que l'inférence selon laquelle si Redis était multicœur, nous pourrions obtenir de bien meilleurs chiffres.