In dit bericht vergelijken we twee van de meest populaire NoSQL-databases:Redis (in-memory) en MongoDB (Percona-geheugenopslagengine).
Redis is een populaire en zeer snelle in-memory databasestructuuropslag die voornamelijk wordt gebruikt als cache of als berichtenmakelaar. Omdat het zich in het geheugen bevindt, is het de favoriete dataopslag wanneer responstijden al het andere overtreffen.
MongoDB is een documentopslag op schijf die een JSON-interface voor gegevens biedt en een zeer uitgebreide querytaal heeft. Het staat bekend om zijn snelheid, efficiëntie en schaalbaarheid en is momenteel de meest populaire NoSQL-database die tegenwoordig wordt gebruikt. Omdat het echter een database op schijf is, kan het qua absolute prestaties niet gunstig afsteken bij een in-memory database zoals Redis. Maar met de beschikbaarheid van de in-memory storage-engines voor MongoDB wordt een meer directe vergelijking mogelijk.
Percona Memory Engine voor MongoDB
Vanaf versie 3.0 biedt MongoDB een API om de opslagengine van uw keuze aan te sluiten. Een opslagengine, uit de MongoDB-context, is het onderdeel van de database dat verantwoordelijk is voor het beheren van de manier waarop de gegevens worden opgeslagen, zowel in het geheugen als op schijf. MongoDB ondersteunt een in-memory storage-engine, maar deze is momenteel beperkt tot de Enterprise-editie van het product. In 2016 bracht Percona een open source in-memory engine uit voor de MongoDB Community Edition, de Percona Memory Engine for MongoDB. Net als de in-memory engine van MonogDB is het ook een variant van de WiredTiger-opslagengine, maar zonder persistentie op schijf.
Met een in-memory MongoDB-opslagengine hebben we een gelijk speelveld tussen Redis en MongoDB. Dus waarom moeten we de twee vergelijken? Laten we eens kijken naar de voordelen van elk van hen als een caching-oplossing.
Laten we eerst naar Redis kijken.
Voordelen van Redis als cache
- Een bekende caching-oplossing die hierin uitblinkt.
- Redis is geen gewone cache-oplossing. Het heeft geavanceerde gegevensstructuren die veel krachtige manieren bieden om gegevens op te slaan en op te vragen, wat niet mogelijk is met een vanille-sleutelwaardecache.
- Redis is vrij eenvoudig in te stellen, te gebruiken en te leren.
- Redis biedt persistentie die u kunt instellen, zodat het opwarmen van de cache in het geval van een crash probleemloos verloopt.
Nadelen van Redis:
- Het heeft geen ingebouwde versleuteling op de draad.
- Geen op rollen gebaseerd accountbeheer (RBAC).
- Er is geen naadloze, volwassen oplossing voor clustering.
- Kan lastig zijn om te implementeren in grootschalige cloudimplementaties.
Voordelen van MongoDB als cache
- MongoDB is een meer traditionele database met geavanceerde functies voor gegevensmanipulatie (denk aan aggregaties en kaartverkleining) en een uitgebreide querytaal.
- SSL, RBAC en scale-out ingebouwd.
- Als u MongoDB al als uw primaire database gebruikt, dalen uw operationele en ontwikkelingskosten omdat u maar één database hoeft te leren en te beheren.
Kijk naar dit bericht van Peter Zaitsev over waar de MongoDB in-memory engine goed zou kunnen passen.
Nadeel van MongoDB:
- Met een in-memory engine biedt het geen persistentie totdat het is geïmplementeerd als een replicaset met persistentie geconfigureerd op de gelezen replica('s).
In dit bericht zullen we ons concentreren op het kwantificeren van de prestatieverschillen tussen Redis en MongoDB . Een kwalitatieve vergelijking en operationele verschillen zullen in volgende berichten worden behandeld.
Redis vs. MongoDB in het geheugen
Prestaties
- Redis presteert aanzienlijk beter voor leesbewerkingen voor alle soorten werkbelastingen, en beter voor schrijfbewerkingen naarmate de werkbelastingen toenemen.
- Hoewel MongoDB alle kernen van het systeem gebruikt, wordt de CPU relatief vroeg gebonden. Hoewel er nog computer beschikbaar was, was het beter in schrijven dan Redis.
- Beide databases zijn uiteindelijk compute-gebonden. Hoewel Redis single-threaded is, krijgt het (meestal) meer gedaan met het draaien op één kern dan MongoDB doet terwijl het alle kernen verzadigt.
- Opnieuw , voor niet-triviale datasets, gebruikt veel meer RAM in vergelijking met MongoDB om dezelfde hoeveelheid gegevens op te slaan.
Configuratie
We hebben YCSB gebruikt om de prestaties te meten en hebben het in het verleden gebruikt om de prestaties van MongoDB te vergelijken en te benchmarken op verschillende cloudproviders en -configuraties. We gaan uit van een basiskennis van YCSB-workloads en -functies in de beschrijving van de testopstelling.
- Type database-instantie: AWS EC2 c4.xlarge met 4 cores, 7,5 GB geheugen en verbeterde netwerken om ervoor te zorgen dat we geen netwerkknelpunten hebben.
- Cliëntcomputer: AWS EC2 c4.xlarge in dezelfde virtual private cloud (VPC) als de databaseservers.
- Opnieuw: Versie 3.2.8 met AOF en RDB uitgeschakeld. Op zichzelf staand.
- MongoDB: Percona Memory Engine gebaseerd op MongoDB versie 3.2.12. Op zichzelf staand.
- Netwerkdoorvoer : Gemeten via iperf zoals aanbevolen door 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
- Werklastdetails
- Werklast invoegen: 100 % schrijven – 2,5 miljoen records
- Werklast A: Update zware werklast – 50%/50% leest/schrijft – 25 miljoen bewerkingen
- Werklast B: Lees vooral werkbelasting – 95%/5% leest/schrijft – 25 miljoen bewerkingen
- Klantbelasting: Doorvoer en latentie gemeten over incrementeel toenemende belastingen die door de client worden gegenereerd. Dit werd gedaan door het aantal YCSB-clientlaadthreads te vergroten, beginnend bij 8 en groeiend in veelvouden van 2
Resultaten
Werklast B-prestaties
Aangezien de primaire use case voor in-memory databases de cache is, gaan we eerst naar Workload B kijken.
Hier zijn de doorvoer-/latentiecijfers van de werkbelasting van 25 miljoen bewerkingen en de verhouding tussen lezen/schrijven was 95%/5%. Dit zou een representatieve cache-leesbelasting zijn:
Opmerking:Doorvoer wordt uitgezet tegen de primaire as (links), terwijl latentie wordt uitgezet tegen de secundaire as (rechts).
Waarnemingen tijdens de Workload B-run:
- Voor MongoDB was de CPU verzadigd vanaf 32 threads. Meer dan 300% gebruik met eencijferige inactiviteitspercentages.
- Voor Redis kwam het CPU-gebruik nooit boven de 95%. Dus Redis presteerde consequent beter dan MongoDB terwijl het op een enkele thread draaide, terwijl MongoDB alle kernen van de machine verzadigde.
- Voor Redis, met 128 threads, mislukten de uitvoeringen vaak met uitzonderingen voor leestime-out.
Werklast een prestatie
Dit zijn de doorvoer-/latentiecijfers van de werkbelasting van 25 miljoen bewerkingen. De verhouding tussen lezen/schrijven was 50%/50%:
Opmerking:Doorvoer wordt uitgezet tegen de primaire as (links), terwijl latentie wordt uitgezet tegen de secundaire as (rechts).
Waarnemingen tijdens de Workload A-run:
- Voor MongoDB was de CPU verzadigd vanaf 32 threads. Meer dan 300% gebruik met eencijferige inactiviteitspercentages.
- Voor Redis is het CPU-gebruik nooit hoger dan 95%.
- Voor Redis, met 64 threads en hoger, mislukten de uitvoeringen vaak met uitzonderingen voor leestime-out.
Voeg werkbelastingprestaties in
Ten slotte zijn hier de doorvoer-/latentiecijfers van de 2,5 miljoen recordinvoegwerklast. Het aantal records is geselecteerd om ervoor te zorgen dat het totale geheugen werd gebruikt in de gebeurtenis Redis die niet hoger was dan 80% (aangezien Redis de geheugenvreter is, zie Bijlage B).
Opmerking:Doorvoer wordt uitgezet tegen de primaire as (links), terwijl latentie wordt uitgezet tegen de secundaire as (rechts).
Waarnemingen tijdens de uitvoering van de werkbelasting invoegen:
- Voor MongoDB was de CPU verzadigd vanaf 32 threads. Meer dan 300% gebruik met eencijferige inactiviteitspercentages.
- Voor Redis is het CPU-gebruik nooit hoger dan 95%.
Bijlagen
A:Single-Thread Prestaties
Ik had een sterke drang om dit uit te zoeken - ook al is het niet erg handig in echte omstandigheden:wie zou er beter zijn als ze dezelfde belasting op elk van hen toepassen vanuit een enkele thread. Dat wil zeggen, hoe zou een single-threaded applicatie presteren?
B:Databasegrootte
De standaardindeling van records die door YCSB zijn ingevoegd, is:elk record bestaat uit 10 velden en elk veld is 100 bytes. Ervan uitgaande dat elk record ongeveer 1 KB is, zou de totale verwachte grootte in het geheugen meer dan 2,4 GB zijn. Er was een schril contrast in de werkelijke maten zoals te zien in de databases.
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 }
De benodigde ruimte is dus ~ 2,7 GB, wat redelijk in de buurt komt van wat we hadden verwacht.
Opnieuw
Laten we nu naar Redis kijken.
> 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
Bij piekgebruik lijkt Redis ongeveer 5,72G geheugen in beslag te nemen, d.w.z. twee keer zoveel geheugen als MongoDB in beslag neemt. Nu is deze vergelijking misschien niet perfect vanwege de verschillen in de twee databases, maar dit verschil in geheugengebruik is te groot om te negeren. YCSB voegt record in een hash in Redis in en een index wordt bijgehouden in een gesorteerde set. Aangezien een individuele invoer groter is dan 64, wordt de hash normaal gecodeerd en wordt er geen ruimte bespaard. Redis-prestaties gaan ten koste van een grotere geheugenvoetafdruk.
Dit kan naar onze mening een belangrijk gegevenspunt zijn bij het kiezen tussen MongoDB en Redis. MongoDB kan de voorkeur hebben voor gebruikers die hun geheugenkosten willen verlagen.
C:netwerkdoorvoer
Een in-memory databaseserver is waarschijnlijk computergebonden of netwerk-I/O-gebonden, dus het was tijdens de hele set van deze tests belangrijk om ervoor te zorgen dat we nooit netwerkgebonden zouden worden. Het meten van de netwerkdoorvoer tijdens het uitvoeren van doorvoertests voor applicaties heeft een negatieve invloed op de algehele doorvoermeting. Daarom voerden we opeenvolgende metingen van de netwerkdoorvoer uit met iftop bij het aantal threads waar de hoogste schrijfdoorvoer werd waargenomen. Dit bleek ongeveer 440 Mbps te zijn voor zowel Redis als MongoDB bij hun respectievelijke piekdoorvoer. Gezien onze initiële meting van de maximale netwerkbandbreedte van ongeveer 1,29 Gbps, zijn we er zeker van dat we nooit de netwerkgrenzen zullen bereiken. In feite ondersteunt het alleen de gevolgtrekking dat als Redis multi-core zou zijn, we veel betere cijfers zouden krijgen.